Exemplo n.º 1
0
/*!
 * Perform wrapping of a black key from a RED slot
 *
 * @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;
	sah_Head_Desc *desc_chain = NULL;
	unsigned slots_allocated = 0;	/* boolean */
	fsl_shw_sko_t T_key_info;	/* for holding T */
	fsl_shw_sko_t KEK_key_info;	/* for holding KEK */

	black_key[LENGTH_OFFSET] = key_info->key_length;
	black_key[ALGORITHM_OFFSET] = key_info->algorithm;

	memcpy(&T_key_info, key_info, sizeof(T_key_info));
	fsl_shw_sko_set_key_length(&T_key_info, T_LENGTH);
	T_key_info.algorithm = FSL_KEY_ALG_HMAC;

	memcpy(&KEK_key_info, &T_key_info, sizeof(KEK_key_info));
	KEK_key_info.algorithm = FSL_KEY_ALG_AES;

	ret = do_scc_slot_alloc(user_ctx, T_LENGTH, key_info->userid,
				&T_key_info.handle);

	if (ret == FSL_RETURN_OK_S) {
		ret = do_scc_slot_alloc(user_ctx, KEK_LENGTH, key_info->userid,
					&KEK_key_info.handle);
		if (ret != FSL_RETURN_OK_S) {
#ifdef DIAG_SECURITY_FUNC
			LOG_DIAG("do_scc_slot_alloc() failed");
#endif
			do_scc_slot_dealloc(user_ctx, key_info->userid,
					    T_key_info.handle);
		} else {
			slots_allocated = 1;
		}

	}

	/* Set up to compute everything except T' ... */
	if (ret == FSL_RETURN_OK_S) {
		uint32_t header;

#ifndef DO_REPEATABLE_WRAP
		/* Compute T = RND() */
		header = SAH_HDR_RNG_GENERATE;	/* Desc. #18 */
		ret = add_key_out_desc(header, &T_key_info, NULL, 0,
				       user_ctx->mem_util, &desc_chain);
#else
		ret = do_scc_slot_load_slot(user_ctx, T_key_info.userid,
					    T_key_info.handle, T_block,
					    T_key_info.key_length);
#endif

		/* Compute KEK = SHA1(T | Ownerid) */
		if (ret == FSL_RETURN_OK_S) {
			sah_Link *link1;
			sah_Link *link2;

			header = (SAH_HDR_MDHA_SET_MODE_HASH	/* #8 */
				  ^ insert_mdha_init
				  ^ insert_mdha_algorithm[FSL_HASH_ALG_SHA1]
				  ^ insert_mdha_pdata);

			/* Input - Start with T */
			ret =
			    sah_Create_Key_Link(user_ctx->mem_util, &link1,
						&T_key_info);

			if (ret == FSL_RETURN_OK_S) {
				/* Still input - append ownerid */
				ret = sah_Append_Link(user_ctx->mem_util, link1,
						      (void *)&key_info->userid,
						      sizeof(key_info->userid),
						      SAH_USES_LINK_DATA);
			}

			if (ret == FSL_RETURN_OK_S) {
				/* Output - KEK goes into RED slot */
				ret =
				    sah_Create_Key_Link(user_ctx->mem_util,
							&link2, &KEK_key_info);
			}

			/* Put the Hash calculation into the chain. */
			if (ret == FSL_RETURN_OK_S) {
				ret =
				    sah_Append_Desc(user_ctx->mem_util,
						    &desc_chain, header, link1,
						    link2);
			}
		}

		/* Compute KEY' = AES-encrypt(KEK, KEY) */
		if (ret == FSL_RETURN_OK_S) {

			header = (SAH_HDR_SKHA_SET_MODE_IV_KEY	/* #1 */
				  ^ insert_skha_mode[FSL_SYM_MODE_CTR]
				  ^ insert_skha_algorithm[FSL_KEY_ALG_AES]
				  ^ insert_skha_modulus[FSL_CTR_MOD_128]);
			/* Set up KEK as key to use */
			ret = add_in_key_desc(header, NULL, 0, &KEK_key_info,
					      user_ctx->mem_util, &desc_chain);

			/* Set up KEY as input, KEY' as output */
			if (ret == FSL_RETURN_OK_S) {
				header = SAH_HDR_SKHA_ENC_DEC;	/* #4 */
				ret = add_key_out_desc(header, key_info,
						       black_key +
						       KEY_PRIME_OFFSET,
						       key_info->key_length,
						       user_ctx->mem_util,
						       &desc_chain);
			}
#ifdef DIAG_SECURITY_FUNC
			if (ret != FSL_RETURN_OK_S) {
				LOG_DIAG
				    ("Creation of sah_Key_Link failed due to bad key"
				     " flag!\n");
			}
#endif				/*DIAG_SECURITY_FUNC */
		}

		/* Compute and store ICV into Black Key */
		if (ret == FSL_RETURN_OK_S) {
			ret =
			    create_icv_calc(user_ctx, &desc_chain, &T_key_info,
					    black_key, key_info->key_length,
					    black_key + ICV_OFFSET);
#ifdef DIAG_SECURITY_FUNC
			if (ret != FSL_RETURN_OK_S) {
				LOG_DIAG
				    ("Creation of sah_Key_Link failed due to bad key"
				     " flag!\n");
			}
#endif				/*DIAG_SECURITY_FUNC */
		}
	}

	/* Now get Sahara to do the work. */
	if (ret == FSL_RETURN_OK_S) {
		ret = sah_Descriptor_Chain_Execute(desc_chain, user_ctx);
#ifdef DIAG_SECURITY_FUNC
		if (ret != FSL_RETURN_OK_S) {
			LOG_DIAG("sah_Descriptor_Chain_Execute() failed");
		}
#endif
	}

	/* Compute T' = SLID_encrypt(T); Result goes to Black Key */
	if (ret == FSL_RETURN_OK_S) {
		ret = do_scc_slot_encrypt(user_ctx, T_key_info.userid,
					  T_key_info.handle,
					  T_LENGTH, black_key + T_PRIME_OFFSET);
#ifdef DIAG_SECURITY_FUNC
		if (ret != FSL_RETURN_OK_S) {
			LOG_DIAG("do_scc_slot_encrypt() failed");
		}
#endif
	}

	if (slots_allocated) {
		do_scc_slot_dealloc(user_ctx, key_info->userid,
				    T_key_info.handle);
		do_scc_slot_dealloc(user_ctx, key_info->userid,
				    KEK_key_info.handle);
	}

	return ret;
}				/* wrap */
Exemplo n.º 2
0
/*!
 * 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 */
/*!
 * Perform wrapping of a black key from a RED slot
 *
 * @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)
{
	SAH_SF_DCLS;
	unsigned slots_allocated = 0;	/* boolean */
	fsl_shw_sko_t T_key_info;	/* for holding T */
	fsl_shw_sko_t KEK_key_info;	/* for holding KEK */
	unsigned original_key_length = key_info->key_length;
	unsigned rounded_key_length;
	sah_Link *link1;
	sah_Link *link2;

	black_key[LENGTH_OFFSET] = key_info->key_length;
	black_key[ALGORITHM_OFFSET] = key_info->algorithm;

	memcpy(&T_key_info, key_info, sizeof(T_key_info));
	fsl_shw_sko_set_key_length(&T_key_info, T_LENGTH);
	T_key_info.algorithm = FSL_KEY_ALG_HMAC;

	memcpy(&KEK_key_info, &T_key_info, sizeof(KEK_key_info));
	KEK_key_info.algorithm = FSL_KEY_ALG_AES;

	if (key_info->keystore == NULL) {
		/* Key goes in system keystore */
		ret = do_system_keystore_slot_alloc(user_ctx,
				T_LENGTH, key_info->userid,
			    &T_key_info.handle);
	
	} else {
		/* Key goes in user keystore */
		ret = keystore_slot_alloc(key_info->keystore,
				  T_LENGTH,
				  key_info->userid, &T_key_info.handle);
	}
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}

	if (key_info->keystore == NULL) {
		/* Key goes in system keystore */
		ret = do_system_keystore_slot_alloc(user_ctx,
				KEK_LENGTH, key_info->userid,
				&KEK_key_info.handle);
		
	} else {
		/* Key goes in user keystore */
		ret = keystore_slot_alloc(key_info->keystore,
			  KEK_LENGTH,  key_info->userid, 
			  &KEK_key_info.handle);
	}

	if (ret != FSL_RETURN_OK_S) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG("do_scc_slot_alloc() failed");
#endif
		if (key_info->keystore == NULL) {
			/* Key goes in system keystore */
			(void)do_system_keystore_slot_dealloc(user_ctx,
				key_info->userid, T_key_info.handle);
	
		} else {
			/* Key goes in user keystore */
			(void)keystore_slot_dealloc(key_info->keystore,
				  	key_info->userid, T_key_info.handle);
		}
	} else {
		slots_allocated = 1;
	}

	/* Set up to compute everything except T' ... */
#ifndef DO_REPEATABLE_WRAP
	/* Compute T = RND() */
	header = SAH_HDR_RNG_GENERATE;	/* Desc. #18 */
	DESC_KEY_OUT(header, &T_key_info, 0, NULL);
#else
	if (key_info->keystore == NULL) {
		/* Key goes in system keystore */
		ret = do_system_keystore_slot_load(user_ctx,
			   T_key_info.userid,
			   T_key_info.handle, T_block,
			   T_key_info.key_length);
	} else {
		/* Key goes in user keystore */
		ret = keystore_slot_load(key_info->keystore,
				 T_key_info.userid,
				 T_key_info.handle,
				 T_block, T_key_info.key_length);
	}

	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}
#endif

	/* Compute KEK = SHA256(T | Ownerid) */
	header = (SAH_HDR_MDHA_SET_MODE_HASH	/* #8 */
		  ^ sah_insert_mdha_init
		  ^ sah_insert_mdha_algorithm[FSL_HASH_ALG_SHA256]
		  ^ sah_insert_mdha_pdata);
	/* Input - Start with T */
	ret = sah_Create_Key_Link(user_ctx->mem_util, &link1, &T_key_info);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}
	/* Still input - append ownerid */
	ret = sah_Append_Link(user_ctx->mem_util, link1,
			      (void *)&key_info->userid,
			      sizeof(key_info->userid), SAH_USES_LINK_DATA);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}
	/* Output - KEK goes into RED slot */
	ret = sah_Create_Key_Link(user_ctx->mem_util, &link2, &KEK_key_info);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}
	/* Put the Hash calculation into the chain. */
	ret = sah_Append_Desc(user_ctx->mem_util, &desc_chain,
			      header, link1, link2);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}
#if defined(NEED_CTR_WORKAROUND)
	rounded_key_length = ROUND_LENGTH(original_key_length);
	key_info->key_length = rounded_key_length;
#else
	rounded_key_length = original_key_length;
#endif
	/* Compute KEY' = AES-encrypt(KEK, KEY) */
	header = (SAH_HDR_SKHA_SET_MODE_IV_KEY	/* #1 */
		  ^ sah_insert_skha_mode[FSL_SYM_MODE_CTR]
		  ^ sah_insert_skha_algorithm[FSL_KEY_ALG_AES]
		  ^ sah_insert_skha_modulus[FSL_CTR_MOD_128]);
	/* Set up KEK as key to use */
	DESC_IN_KEY(header, 0, NULL, &KEK_key_info);
	header = SAH_HDR_SKHA_ENC_DEC;
	DESC_KEY_OUT(header, key_info,
		     key_info->key_length, black_key + KEY_PRIME_OFFSET);

	/* 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;
	}

	/* Compute and store ICV into Black Key */
	ret = create_icv_calc(user_ctx, &desc_chain, &T_key_info,
			      black_key, original_key_length,
			      black_key + ICV_OFFSET);
	if (ret != FSL_RETURN_OK_S) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG("Creation of sah_Key_Link failed due to bad key"
			 " flag!\n");
#endif				/*DIAG_SECURITY_FUNC */
		goto out;
	}

	/* Now get Sahara to do the work. */
#ifdef DIAG_SECURITY_FUNC
	LOG_DIAG("Encrypting key with KEK");
#endif
	SAH_SF_EXECUTE();
	if (ret != FSL_RETURN_OK_S) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG("sah_Descriptor_Chain_Execute() failed");
#endif
		goto out;
	}

	/* Compute T' = SLID_encrypt(T); Result goes to Black Key */
	if (key_info->keystore == NULL) {
		/* Key goes in system keystore */
		ret = do_system_keystore_slot_encrypt(user_ctx,
					T_key_info.userid,  T_key_info.handle,
					T_LENGTH, black_key + T_PRIME_OFFSET);
	} else {
		/* Key goes in user keystore */
		ret = keystore_slot_encrypt(user_ctx,
					    key_info->keystore,
					    T_key_info.userid,
					    T_key_info.handle,
					    T_LENGTH,
				  	    black_key + T_PRIME_OFFSET);
	}

	if (ret != FSL_RETURN_OK_S) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG("do_scc_slot_encrypt() failed");
#endif
		goto out;
	}

      out:
	key_info->key_length = original_key_length;

	SAH_SF_DESC_CLEAN();
	if (slots_allocated) {
		if (key_info->keystore == NULL) {
			/* Key goes in system keystore */
			(void)do_system_keystore_slot_dealloc(user_ctx,
							      key_info->userid,
							      T_key_info.
							      handle);
			(void)do_system_keystore_slot_dealloc(user_ctx,
							      key_info->userid,
							      KEK_key_info.
							      handle);
		} else {
			/* Key goes in user keystore */
			(void)keystore_slot_dealloc(key_info->keystore,
						    key_info->userid,
				    T_key_info.handle);
			(void)keystore_slot_dealloc(key_info->keystore,
				    key_info->userid,
				    KEK_key_info.handle);
	    }
	}	

	return ret;
}				/* wrap */
Exemplo n.º 4
0
/*!
 * Perform unwrapping of a black key into a RED slot
 *
 * @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 unwrapped... key length, slot info, etc.
 * @param         black_key     Encrypted key
 *
 * @return    A return code of type #fsl_shw_return_t.
 */
static fsl_shw_return_t unwrap(fsl_shw_uco_t * user_ctx,
			       fsl_shw_sko_t * key_info,
			       const uint8_t * black_key)
{
	fsl_shw_return_t ret;
	uint8_t hmac[ICV_LENGTH];
	uint8_t T[T_LENGTH];
	uint8_t kek[KEK_LENGTH + 20];
	int key_length = black_key[LENGTH_OFFSET];
	int rounded_key_length = ROUND_LENGTH(key_length);
	uint8_t key[rounded_key_length];
	fsl_shw_sko_t t_key_info;
	fsl_shw_scco_t t_key_ctx;
	fsl_shw_sko_t kek_key_info;
	fsl_shw_scco_t kek_ctx;
	int unwrapping_sw_key = key_info->flags & FSL_SKO_KEY_SW_KEY;
	int pk_needs_restoration = 0;	/* bool */
	unsigned original_key_length = key_info->key_length;
	int pk_was_held = 0;
	uint8_t current_pk[21];
	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;
	}
#ifdef DIAG_SECURITY_FUNC
	dump("black", black_key, KEY_PRIME_OFFSET + key_length);
#endif
	/* Validate SW flags to prevent misuse */
		if ((key_info->flags & FSL_SKO_KEY_SW_KEY)
			&& !(black_key[FLAGS_OFFSET] & FLAGS_SW_KEY)) {
			ret = FSL_RETURN_BAD_FLAG_S;
			goto out;
		}

	/* Compute T = 3des-dec-ecb(wrap_key, T') */
	init_wrap_key(user_ctx->wrap_key, &t_key_info, &t_key_ctx);
	ret = fsl_shw_symmetric_decrypt(user_ctx, &t_key_info, &t_key_ctx,
					T_LENGTH,
					black_key + T_PRIME_OFFSET, T);
	if (ret != FSL_RETURN_OK_S) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG("Recovery of nonce (T) failed");
#endif				/*DIAG_SECURITY_FUNC */
		goto out;
	}

	/* Compute ICV = HMAC(T, ownerid | len | alg | flags | key' */
	ret = calc_icv(T, (uint8_t *) & key_info->userid,
		       sizeof(key_info->userid),
		       black_key, original_key_length, hmac);

	if (ret != FSL_RETURN_OK_S) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG("Calculation of ICV failed");
#endif				/*DIAG_SECURITY_FUNC */
		goto out;
	}
#ifdef DIAG_SECURITY_FUNC
	LOG_DIAG("Validating MAC of wrapped key");
#endif

	/* Check computed ICV against value in Black Key */
	if (memcmp(black_key + ICV_OFFSET, hmac, ICV_LENGTH) != 0) {
#ifdef DIAG_SECURITY_FUNC
		LOG_DIAG("Computed ICV fails validation\n");
#endif
		ret = FSL_RETURN_AUTH_FAILED_S;
		goto out;
	}

	/* 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) {
		goto out;
	}

	if (unwrapping_sw_key) {
		di_code = dryice_get_programmed_key(current_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;
		}
	}

	/*
	 * "Establish" the KEK in the PK.  If the PK was held and unwrapping a
	 * software key, then release it and try again, but remember that we need
	 * to leave it 'held' if we are unwrapping a software key.
	 *
	 * If the PK is held while we are unwrapping a key for the PK, then
	 * the user didn't call release, so gets an error.
	 */
	di_code = dryice_set_programmed_key(kek, 8 * 21, 0);
	if ((di_code == DI_ERR_INUSE) && unwrapping_sw_key) {
		/* 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;
	}

	if (unwrapping_sw_key) {
		pk_needs_restoration = 1;
	}
	dryice_release_programmed_key();	/* Because of previous 'set' */

	/* Compute KEY = TDES-decrypt(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);
#ifdef DIAG_SECURITY_FUNC
	dump("KEY'", black_key + KEY_PRIME_OFFSET, rounded_key_length);
#endif
	ret = fsl_shw_symmetric_decrypt(user_ctx, &kek_key_info, &kek_ctx,
					rounded_key_length,
					black_key + KEY_PRIME_OFFSET, key);
	if (ret != FSL_RETURN_OK_S) {
		goto out;
	}
#ifdef DIAG_SECURITY_FUNC
	dump("KEY", key, original_key_length);
#endif
	/* Now either put key into PK or into a slot */
	if (key_info->flags & FSL_SKO_KEY_SW_KEY) {
		ret = load_slot(user_ctx, key_info, key);
	} else {
		/*
		 * Since we have just unwrapped a program key, it had
		 * to have been wrapped as a program key, so it must
		 * be 168 bytes long and permuted ...
		 */
		ret = dryice_set_programmed_key(key, 8 * key_length, 0);
		if (ret != FSL_RETURN_OK_S) {
			goto out;
		}
	}

      out:
	key_info->key_length = original_key_length;

	if (pk_needs_restoration) {
		di_code = dryice_set_programmed_key(current_pk, 8 * 21, 0);
	}

	if (!pk_was_held) {
		dryice_release_programmed_key();
	}

	/* Erase tracks of confidential data */
	memset(T, 0, T_LENGTH);
	memset(key, 0, rounded_key_length);
	memset(current_pk, 0, sizeof(current_pk));
	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, KEK_LENGTH);

	return ret;
}				/* unwrap */