/*! * Wrap a key and retrieve the wrapped value. * * A wrapped key is a key that has been cryptographically obscured. It is * only able to be used with #fsl_shw_establish_key(). * * This function will also release the key (see #fsl_shw_release_key()) so * that it must be re-established before reuse. * * This feature is not available for all platforms, nor for all algorithms and * modes. * * @param user_ctx A user context from #fsl_shw_register_user(). * @param key_info The information about the key to be deleted. * @param[out] covered_key The location to store the 48-octet wrapped key. * (This size is based upon the maximum key size * of 32 octets). * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, uint8_t * covered_key) { fsl_shw_return_t ret; /* perform sanity check on the uco */ ret = sah_validate_uco(user_ctx); if (ret == FSL_RETURN_OK_S) { /* For now, only blocking mode calls are supported */ if (user_ctx->flags & FSL_UCO_BLOCKING_MODE) { if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) { ret = wrap(user_ctx, key_info, covered_key); /* Need to deallocate on successful extraction */ if (ret == FSL_RETURN_OK_S) { do_scc_slot_dealloc(user_ctx, key_info->userid, key_info->handle); /* Mark key not available in the flags */ key_info->flags &= ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT); } } } } return ret; }
/*! * Retrieve results from earlier operations. * * @param user_ctx The user's context. * @param result_size The number of array elements of @a results. * @param[in,out] results Pointer to first of the (array of) locations to * store results. * @param[out] result_count Pointer to store the number of results which * were returned. * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_get_results(fsl_shw_uco_t* user_ctx, unsigned result_size, fsl_shw_result_t results[], unsigned* result_count) { fsl_shw_return_t status; /* perform a sanity check on the uco */ status = sah_validate_uco(user_ctx); /* if uco appears ok, build structure and pass to get results */ if (status == FSL_RETURN_OK_S) { sah_results arg; /* if requested is zero, it's done before it started */ if (result_size > 0) { arg.requested = result_size; arg.actual = result_count; arg.results = results; /* get the results */ status = sah_get_results(&arg, user_ctx); } } return status; }
/*! * Compute symmetric decryption * * * @param user_ctx * @param key_info * @param sym_ctx * @param length * @param pt * @param ct * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_symmetric_decrypt(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, fsl_shw_scco_t * sym_ctx, uint32_t length, const uint8_t * ct, uint8_t * pt) { fsl_shw_return_t ret; /* perform sanity check on uco */ ret = sah_validate_uco(user_ctx); if (ret == FSL_RETURN_OK_S) { ret = do_symmetric(user_ctx, key_info, sym_ctx, SYM_DECRYPT, length, ct, pt); } return ret; }
/*! * De-establish a key so that it can no longer be accessed. * * The key will need to be re-established before it can again be used. * * This feature is not available for all platforms, nor for all algorithms and * modes. * * @param user_ctx A user context from #fsl_shw_register_user(). * @param key_info The information about the key to be deleted. * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info) { fsl_shw_return_t ret; /* perform sanity check on the uco */ ret = sah_validate_uco(user_ctx); if (ret == FSL_RETURN_OK_S) { if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) { ret = do_scc_slot_dealloc(user_ctx, key_info->userid, key_info->handle); key_info->flags &= ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT); } } return ret; }
/*! * Sends a request to register this user * * @brief Sends a request to register this user * * @param[in,out] user_ctx part of the structure contains input parameters and * part is filled in by the driver * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t sah_register(fsl_shw_uco_t * user_ctx) { fsl_shw_return_t status; /* this field is used in user mode to indicate a file open has occured. * it is used here, in kernel mode, to indicate that the uco is registered */ user_ctx->sahara_openfd = 0; /* set to 'registered' */ user_ctx->mem_util = &std_kernelmode_mem_util; /* check that uco is valid */ status = sah_validate_uco(user_ctx); /* If life is good, register this user */ if (status == FSL_RETURN_OK_S) { status = sah_handle_registration(user_ctx); } if (status != FSL_RETURN_OK_S) { user_ctx->sahara_openfd = -1; /* set to 'not registered' */ } return status; }
/*! * Place a key into a protected location for use only by cryptographic * algorithms. * * This only needs to be used to a) unwrap a key, or b) set up a key which * could be wrapped with a later call to #fsl_shw_extract_key(). Normal * cleartext keys can simply be placed into #fsl_shw_sko_t key objects with * #fsl_shw_sko_set_key() and used directly. * * The maximum key size supported for wrapped/unwrapped keys is 32 octets. * (This is the maximum reasonable key length on Sahara - 32 octets for an HMAC * key based on SHA-256.) The key size is determined by the @a key_info. The * expected length of @a key can be determined by * #fsl_shw_sko_calculate_wrapped_size() * * The protected key will not be available for use until this operation * successfully completes. * * This feature is not available for all platforms, nor for all algorithms and * modes. * * @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 established. In the create case, the key * length must be set. * @param establish_type How @a key will be interpreted to establish a * key for use. * @param key If @a establish_type is #FSL_KEY_WRAP_UNWRAP, * this is the location of a wrapped key. If * @a establish_type is #FSL_KEY_WRAP_CREATE, this * parameter can be @a NULL. If @a establish_type * is #FSL_KEY_WRAP_ACCEPT, this is the location * of a plaintext key. * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_establish_key(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, fsl_shw_key_wrap_t establish_type, const uint8_t * key) { fsl_shw_return_t ret; sah_Head_Desc *desc_chain = NULL; uint32_t header = SAH_HDR_RNG_GENERATE; /* Desc. #18 for rand */ unsigned original_key_length = key_info->key_length; unsigned rounded_key_length; /* Write operations into SCC memory require word-multiple number of * bytes. For ACCEPT and CREATE functions, the key length may need * to be rounded up. Calculate. */ if ((original_key_length & 3) != 0) { rounded_key_length = original_key_length + 4 - (original_key_length & 3); } else { rounded_key_length = original_key_length; } /* perform sanity check on the uco */ ret = sah_validate_uco(user_ctx); if (ret != FSL_RETURN_OK_S) { return ret; } ret = do_scc_slot_alloc(user_ctx, key_info->key_length, key_info->userid, &key_info->handle); if (ret == FSL_RETURN_OK_S) { key_info->flags |= FSL_SKO_KEY_ESTABLISHED; switch (establish_type) { case FSL_KEY_WRAP_CREATE: /* Use safe version of key length */ key_info->key_length = rounded_key_length; /* Generate descriptor to put random value into */ ret = add_key_out_desc(header, key_info, NULL, 0, user_ctx->mem_util, &desc_chain); /* Restore actual, desired key length */ key_info->key_length = original_key_length; if (ret == FSL_RETURN_OK_S) { uint32_t old_flags = user_ctx->flags; /* Now put random value into key */ ret = sah_Descriptor_Chain_Execute(desc_chain, user_ctx); /* Restore user's old flag value */ user_ctx->flags = old_flags; } #ifdef DIAG_SECURITY_FUNC else { LOG_DIAG ("Creation of sah_Key_Link failed due to bad" " key flag!\n"); } #endif /*DIAG_SECURITY_FUNC */ break; case FSL_KEY_WRAP_ACCEPT: if (key == NULL) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("ACCEPT: Red Key is NULL"); #endif ret = FSL_RETURN_ERROR_S; } else { /* Copy in safe number of bytes of Red key */ ret = do_scc_slot_load_slot(user_ctx, key_info->userid, key_info->handle, key, rounded_key_length); } break; case FSL_KEY_WRAP_UNWRAP: /* For now, disallow non-blocking calls. */ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) { ret = FSL_RETURN_BAD_FLAG_S; } else if (key == NULL) { ret = FSL_RETURN_ERROR_S; } else { ret = unwrap(user_ctx, key_info, key); if (ret != FSL_RETURN_OK_S) { } } break; default: ret = FSL_RETURN_BAD_FLAG_S; break; } /* switch */ if (ret != FSL_RETURN_OK_S) { fsl_shw_return_t scc_err; scc_err = do_scc_slot_dealloc(user_ctx, key_info->userid, key_info->handle); key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED; } } return ret; }
/*! * Get a random number * * * @param user_ctx * @param length * @param data * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx, uint32_t length, uint8_t * data) { SAH_SF_DCLS; /* perform a sanity check on the uco */ ret = sah_validate_uco(user_ctx); if (ret != FSL_RETURN_OK_S) { goto out; } /* There is a possible problem with the auto-seed that automatically * reseeds every 4094x1024 bytes (2^20 words). * The third generation does not occur for an unknown reason so far. * Different method for manual reseed could be used, such every XXhours, * or every YY calls of this function a seed is generated. * The choosen method counts the number of generated random bytes, and * transparently reseed every 16MB. * Note that if the auto-seed is not disabled in the SAHARA driver, the * manual seed should be done after less generated bytes (see below). */ g_gen_rnd_bytes += length; #ifndef DISABLE_AUTOSEED /* use that value if auto-reseed is set */ if(!(g_gen_rnd_bytes % (4094*1024))) { #else /* otherwise use a larger but still satisfying value */ if(!(g_gen_rnd_bytes % (0x1000000))) { #endif //DISABLE_AUTOSEED header = SAH_HDR_FORCE_SEED; DESC_OUT_OUT(header, 0, NULL, 0, NULL); SAH_SF_EXECUTE(); SAH_SF_DESC_CLEAN(); } header = SAH_HDR_RNG_GENERATE; /* Desc. #18 */ DESC_OUT_OUT(header, length, data, 0, NULL); SAH_SF_EXECUTE(); out: SAH_SF_DESC_CLEAN(); return ret; } #ifdef __KERNEL__ EXPORT_SYMBOL(fsl_shw_add_entropy); #endif /*! * Add entropy to a random number generator * @param user_ctx * @param length * @param data * * @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) { SAH_SF_DCLS; /* perform a sanity check on the uco */ ret = sah_validate_uco(user_ctx); if (ret != FSL_RETURN_OK_S) { goto out; } header = SAH_HDR_RNG_GENERATE; /* Desc. #18 */ /* create descriptor #18. Generate random data */ DESC_IN_IN(header, 0, NULL, length, data) SAH_SF_EXECUTE(); out: SAH_SF_DESC_CLEAN(); return ret; }
/*! * Get the precompute information * * * @param user_ctx * @param key_info * @param hmac_ctx * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_hmac_precompute( fsl_shw_uco_t* user_ctx, fsl_shw_sko_t* key_info, fsl_shw_hmco_t* hmac_ctx) { fsl_shw_return_t status; sah_Head_Desc* desc_chain = NULL; uint32_t header; /* perform sanity check on uco */ status = sah_validate_uco(user_ctx); if (status != FSL_RETURN_OK_S) { return status; } if ((key_info->algorithm != FSL_KEY_ALG_HMAC) || (key_info->key_length > 64)) { return FSL_RETURN_BAD_ALGORITHM_S; } else if (key_info->key_length == 0) { return FSL_RETURN_BAD_KEY_LENGTH_S; } /* Set up to start the Inner Calculation */ /* Desc. #8 w/IPAD, & INIT */ header = SAH_HDR_MDHA_SET_MODE_HASH ^ insert_mdha_ipad ^ insert_mdha_init ^ insert_mdha_algorithm[hmac_ctx->algorithm]; status = add_key_out_desc(header, key_info, (uint8_t*)hmac_ctx->inner_precompute, hmac_ctx->context_register_length, user_ctx->mem_util, &desc_chain); /* Set up for starting Outer calculation */ if (status == FSL_RETURN_OK_S) { /* exchange IPAD bit for OPAD bit */ header ^= (insert_mdha_ipad ^ insert_mdha_opad); /* Theoretically, we can leave this link out and use the key which is * already in the register... however, if we do, the resulting opad * hash does not have the correct value when using the model. */ status = add_key_out_desc(header, key_info, (uint8_t*)hmac_ctx->outer_precompute, hmac_ctx->context_register_length, user_ctx->mem_util, &desc_chain); } /* Pass the chain to the driver. */ if (status == FSL_RETURN_OK_S) { status = sah_Descriptor_Chain_Execute(desc_chain, user_ctx); if (status == FSL_RETURN_OK_S) { /* flag that precomputes have been entered in this hco * assume it'll first be used for initilizing */ hmac_ctx->flags |= (FSL_HMAC_FLAGS_INIT | FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT); } } else { sah_Descriptor_Chain_Destroy(user_ctx->mem_util, &desc_chain); } return status; }
/*! * Get the hmac * * * @param user_ctx Info for acquiring memory * @param key_info * @param hmac_ctx * @param msg * @param length * @param result * @param result_len * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t fsl_shw_hmac( fsl_shw_uco_t* user_ctx, fsl_shw_sko_t* key_info, fsl_shw_hmco_t* hmac_ctx, const uint8_t* msg, uint32_t length, uint8_t* result, uint32_t result_len) { fsl_shw_return_t ret; sah_Head_Desc* desc_chain = NULL; uint32_t header; /* perform sanity check on uco */ ret = sah_validate_uco(user_ctx); /* check flag consistency */ /* Note that Final, Init, and Save are an illegal combination when a key * is being used. Because of the logic flow of this routine, that is * taken care of without being explict */ if (ret == FSL_RETURN_OK_S) { if ( /* nothing to work on */ (((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) == 0) && ((hmac_ctx->flags & FSL_HMAC_FLAGS_LOAD) == 0)) || /* can't do both */ ((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) && (hmac_ctx->flags & FSL_HMAC_FLAGS_LOAD)) || /* must be some output */ (((hmac_ctx->flags & FSL_HMAC_FLAGS_SAVE) == 0) && ((hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) == 0))) { ret = FSL_RETURN_BAD_FLAG_S; } } /* build descriptor #6 */ if (ret == FSL_RETURN_OK_S) { /* start building descriptor header */ header = SAH_HDR_MDHA_SET_MODE_MD_KEY ^ insert_mdha_algorithm[hmac_ctx->algorithm] ^ insert_mdha_init; /* if this is to finalize the digest, mark to pad last block */ if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) { header ^= insert_mdha_pdata; } /*** Check if this is a one shot ***/ if ((hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) && (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE)) { header ^= insert_mdha_hmac; /*** See if this uses Precomputes ***/ if (hmac_ctx->flags & FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT) { ret = add_two_in_desc(header, (uint8_t *)hmac_ctx->inner_precompute, hmac_ctx->context_register_length, (uint8_t *)hmac_ctx->outer_precompute, hmac_ctx->context_length, user_ctx->mem_util, &desc_chain); } else { /*** Precomputes not requested, try using Key ***/ if (key_info->key != NULL) { /* first, validate the key fields and related flag */ if (key_info->key_length == 0) { ret = FSL_RETURN_BAD_KEY_LENGTH_S; } else { if ((key_info->algorithm != FSL_KEY_ALG_HMAC) || (key_info->key_length > 64)) { ret = FSL_RETURN_BAD_ALGORITHM_S; } } if (ret == FSL_RETURN_OK_S) { /* finish building descriptor header (Key specific) */ header ^= insert_mdha_mac_full; ret = add_in_key_desc(header, NULL, 0, key_info, user_ctx->mem_util, &desc_chain); } } else { /*** not using Key or Precomputes, so die ***/ ret = FSL_RETURN_BAD_FLAG_S; } } } else { /*** it's not a one shot, must be multi-step ***/ /* this the last chunk? */ if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) { header ^= insert_mdha_hmac; ret = add_two_in_desc(header, (uint8_t *)hmac_ctx->ongoing_context, hmac_ctx->context_register_length, (uint8_t *)hmac_ctx->outer_precompute, hmac_ctx->context_length, user_ctx->mem_util, &desc_chain); } else { /* not last chunk */ uint8_t *ptr1; if (hmac_ctx->flags & FSL_HMAC_FLAGS_INIT) { /* must be using precomputes, cannot 'chunk' message * starting with a key */ if (hmac_ctx->flags & FSL_HMAC_FLAGS_PRECOMPUTES_PRESENT) { ptr1 = (uint8_t *)hmac_ctx->inner_precompute; } else { ret = FSL_RETURN_NO_RESOURCE_S; } } else { ptr1 = (uint8_t *)hmac_ctx->ongoing_context; } if (ret == FSL_RETURN_OK_S) { ret = add_two_in_desc(header, ptr1, hmac_ctx->context_register_length, NULL, 0, user_ctx->mem_util, &desc_chain); } } } } /* end of building descriptor #6 */ /*** build descriptor #10 & maybe 11 ***/ if (ret == FSL_RETURN_OK_S) { header = SAH_HDR_MDHA_HASH; if (hmac_ctx->flags & FSL_HMAC_FLAGS_FINALIZE) { /* check that the results parameters seem reasonable */ if ((result_len != 0) && (result != NULL)) { if (result_len > hmac_ctx->context_register_length) { result_len = hmac_ctx->context_register_length; } /* message in / digest out (descriptor #10) */ ret = add_in_out_desc(header, msg, length, result, result_len, user_ctx->mem_util, &desc_chain); /*** see if descriptor #11 needs to be built ***/ if ((hmac_ctx->flags & FSL_HMAC_FLAGS_SAVE) && (ret == FSL_RETURN_OK_S)) { header = SAH_HDR_MDHA_STORE_DIGEST; /* nothing in / context out */ ret = add_two_in_desc(header, NULL, 0, (uint8_t *)hmac_ctx->ongoing_context, hmac_ctx->context_register_length, user_ctx->mem_util, &desc_chain); } } else { /* something wrong with result or its length */ ret = FSL_RETURN_BAD_DATA_LENGTH_S; } } else { /* finalize not set, so store in ongoing context field */ if ((length % 64) == 0) { /* message in / context out */ ret = add_in_out_desc(header, msg, length, (uint8_t *)hmac_ctx->ongoing_context, hmac_ctx->context_register_length, user_ctx->mem_util, &desc_chain); } else { ret = FSL_RETURN_BAD_DATA_LENGTH_S; } } } /* Pass the chain to the driver. */ if (ret == FSL_RETURN_OK_S) { ret = sah_Descriptor_Chain_Execute(desc_chain, user_ctx); } else { if (desc_chain != NULL) { /* ignore the destroy function's return value, the destroy * function's return value is not what went wroung with this * routine */ sah_Descriptor_Chain_Destroy(user_ctx->mem_util, &desc_chain); } } return ret; }