/* * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ /*! * @file fsl_shw_wrap.c * * This file implements Key-Wrap (Black Key) and Key Establishment functions of * the FSL SHW API for the SHW (non-SAHARA) driver. * * This is the Black Key information: * *
BLACK_KEY =ICV | T' | LEN | ALG | FLAGS | KEY'
 
To Wrap
T = RND()16
KEK=HASHsha256(T | Ownerid)16
KEY'= TDEScbc-enc(Key=KEK, Data=KEY, IV=Ownerid)
ICV=HMACsha256 (Key=T, Data=Ownerid | LEN | ALG | FLAGS | KEY')16
T'=TDESecb-enc (Key=Wrap_Key, IV=Ownerid, Data=T)
 
To Unwrap
T=TDESecb-dec (Key=Wrap_Key, IV=Ownerid, Data=T')
ICV=HMACsha256 (Key=T, Data=Ownerid | LEN | ALG | FLAGS | KEY')16
KEK=HASHsha256 (T | Ownerid)16
KEY=TDEScbc-dec (Key=KEK, Data=KEY', IV=Ownerid)
* This code supports two types of keys: Software Keys and keys destined for * (or residing in) the DryIce Programmed Key Register. * * Software Keys go to / from the keystore. * * PK keys go to / from the DryIce Programmed Key Register. * * This code only works on a platform with DryIce. "software" keys go into * the keystore. "Program" keys go to the DryIce Programmed Key Register. * As far as this code is concerned, the size of that register is 21 bytes, * the size of a 3DES key with parity stripped. * * The maximum key size supported for wrapped/unwrapped keys depends upon * LENGTH_LENGTH. Currently, it is one byte, so the maximum key size is * 255 bytes. However, key objects cannot currently hold a key of this * length, so a smaller key size is the max. */ #include "fsl_platform.h" /* This code only works in kernel mode */ #include "shw_driver.h" #ifdef DIAG_SECURITY_FUNC #include "apihelp.h" #endif #if defined(__KERNEL__) && defined(FSL_HAVE_DRYICE) #include "../dryice.h" #include #include "portable_os.h" #include "fsl_shw_keystore.h" #include #include "shw_hmac.h" #include "shw_hash.h" #define ICV_LENGTH 16 #define T_LENGTH 16 #define KEK_LENGTH 21 #define LENGTH_LENGTH 1 #define ALGORITHM_LENGTH 1 #define FLAGS_LENGTH 1 /* ICV | T' | LEN | ALG | FLAGS | KEY' */ #define ICV_OFFSET 0 #define T_PRIME_OFFSET (ICV_OFFSET + ICV_LENGTH) #define LENGTH_OFFSET (T_PRIME_OFFSET + T_LENGTH) #define ALGORITHM_OFFSET (LENGTH_OFFSET + LENGTH_LENGTH) #define FLAGS_OFFSET (ALGORITHM_OFFSET + ALGORITHM_LENGTH) #define KEY_PRIME_OFFSET (FLAGS_OFFSET + FLAGS_LENGTH) #define FLAGS_SW_KEY 0x01 #define LENGTH_PATCH 8 #define LENGTH_PATCH_MASK (LENGTH_PATCH - 1) /*! rounded up from 168 bits to the next word size */ #define HW_KEY_LEN_WORDS_BITS 192 /*! * Round a key length up to the TDES block size * * @param len Length of key, in bytes * * @return Length rounded up, if necessary */ #define ROUND_LENGTH(len) \ ({ \ uint32_t orig_len = len; \ uint32_t new_len; \ \ if ((orig_len & LENGTH_PATCH_MASK) != 0) { \ new_len = (orig_len + LENGTH_PATCH \ - (orig_len & LENGTH_PATCH_MASK)); \ } \ else { \ new_len = orig_len; \ } \ \ new_len; \ }) /* This is the system keystore object */ extern fsl_shw_kso_t system_keystore; #ifdef DIAG_SECURITY_FUNC static void dump(const char *name, const uint8_t * data, unsigned int len) { os_printk("%s: ", name); while (len > 0) { os_printk("%02x ", (unsigned)*data++); len--; } os_printk("\n"); } #endif /* * For testing of the algorithm implementation,, the DO_REPEATABLE_WRAP flag * causes the T_block to go into the T field during a wrap operation. This * will make the black key value repeatable (for a given SCC secret key, or * always if the default key is in use). * * Normally, a random sequence is used. */ #ifdef DO_REPEATABLE_WRAP /*! * Block of zeroes which is maximum Symmetric block size, used for * initializing context register, etc. */ static uint8_t T_block[16] = { 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42, 0x42, 0, 0, 0x42 }; #endif EXPORT_SYMBOL(fsl_shw_establish_key); EXPORT_SYMBOL(fsl_shw_read_key); EXPORT_SYMBOL(fsl_shw_extract_key); EXPORT_SYMBOL(fsl_shw_release_key); extern fsl_shw_return_t alloc_slot(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info); extern fsl_shw_return_t load_slot(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, const uint8_t * key); extern fsl_shw_return_t dealloc_slot(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info); /*! * Initalialize SKO and SCCO used for T <==> T' cipher operation * * @param wrap_key Which wrapping key user wants * @param key_info Key object for selecting wrap key * @param wrap_ctx Sym Context object for doing the cipher op */ static inline void init_wrap_key(fsl_shw_pf_key_t wrap_key, fsl_shw_sko_t * key_info, fsl_shw_scco_t * wrap_ctx) { fsl_shw_sko_init_pf_key(key_info, FSL_KEY_ALG_TDES, wrap_key); fsl_shw_scco_init(wrap_ctx, FSL_KEY_ALG_TDES, FSL_SYM_MODE_ECB); } /*! * Insert descriptors to calculate ICV = HMAC(key=T, data=LEN|ALG|KEY') * * @param user_ctx A user context from #fsl_shw_register_user(). * @param T Location of nonce (length is T_LENGTH bytes) * @param userid Location of userid/ownerid * @param userid_len Length, in bytes of @c userid * @param black_key Beginning of Black Key region * @param key_length Number of bytes of key' there are in @c black_key * @param[out] hmac Location to store ICV. Will be tagged "USES" so * sf routines will not try to free it. * * @return A return code of type #fsl_shw_return_t. */ static fsl_shw_return_t calc_icv(const uint8_t * T, const uint8_t * userid, unsigned int userid_len, const uint8_t * black_key, uint32_t key_length, uint8_t * hmac) { fsl_shw_return_t code; shw_hmac_state_t hmac_state; /* Load up T as key for the HMAC */ code = shw_hmac_init(&hmac_state, T, T_LENGTH); if (code != FSL_RETURN_OK_S) { goto out; } /* Previous step loaded key; Now set up to hash the data */ /* Input - start with ownerid */ code = shw_hmac_update(&hmac_state, userid, userid_len); if (code != FSL_RETURN_OK_S) { goto out; } /* Still input - Append black-key fields len, alg, key' */ code = shw_hmac_update(&hmac_state, (void *)black_key + LENGTH_OFFSET, (LENGTH_LENGTH + ALGORITHM_LENGTH + FLAGS_LENGTH + key_length)); if (code != FSL_RETURN_OK_S) { goto out; } /* Output - computed ICV/HMAC */ code = shw_hmac_final(&hmac_state, hmac, ICV_LENGTH); if (code != FSL_RETURN_OK_S) { goto out; } out: return code; } /* calc_icv */ /*! * Compute and return the KEK (Key Encryption Key) from the inputs * * @param userid The user's 'secret' for the key * @param userid_len Length, in bytes of @c userid * @param T The nonce * @param[out] kek Location to store the computed KEK. It will * be 21 bytes long. * * @return the usual error code */ static fsl_shw_return_t calc_kek(const uint8_t * userid, unsigned int userid_len, const uint8_t * T, uint8_t * kek) { fsl_shw_return_t code = FSL_RETURN_INTERNAL_ERROR_S; shw_hash_state_t hash_state; code = shw_hash_init(&hash_state, FSL_HASH_ALG_SHA256); if (code != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("Hash init failed: %s\n", fsl_error_string(code)); #endif goto out; } code = shw_hash_update(&hash_state, T, T_LENGTH); if (code != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("Hash for T failed: %s\n", fsl_error_string(code)); #endif goto out; } code = shw_hash_update(&hash_state, userid, userid_len); if (code != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("Hash for userid failed: %s\n", fsl_error_string(code)); #endif goto out; } code = shw_hash_final(&hash_state, kek, KEK_LENGTH); if (code != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("Could not extract kek: %s\n", fsl_error_string(code)); #endif goto out; } #if KEK_LENGTH != 21 { uint8_t permuted_kek[21]; fsl_shw_permute1_bytes(kek, permuted_kek, KEK_LENGTH / 8); memcpy(kek, permuted_kek, 21); memset(permuted_kek, 0, sizeof(permuted_kek)); } #endif #ifdef DIAG_SECURITY_FUNC dump("kek", kek, 21); #endif out: return code; } /* end fn calc_kek */ /*! * Validate user's wrap key selection * * @param wrap_key The user's desired wrapping key */ static fsl_shw_return_t check_wrap_key(fsl_shw_pf_key_t wrap_key) { /* unable to use desired key */ fsl_shw_return_t ret = FSL_RETURN_NO_RESOURCE_S; if ((wrap_key != FSL_SHW_PF_KEY_IIM) && (wrap_key != FSL_SHW_PF_KEY_RND) && (wrap_key != FSL_SHW_PF_KEY_IIM_RND)) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Invalid wrap_key in key wrap/unwrap attempt"); #endif goto out; } ret = FSL_RETURN_OK_S; out: return ret; } /* end fn check_wrap_key */ /*! * 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 */ /*! * 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 */ static fsl_shw_return_t create(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info) { fsl_shw_return_t ret = FSL_RETURN_ERROR_S; unsigned key_length = key_info->key_length; di_return_t di_code; if (!(key_info->flags & FSL_SKO_KEY_SW_KEY)) { /* Must be creating key for PK */ if ((key_info->algorithm != FSL_KEY_ALG_TDES) || ((key_info->key_length != 16) && (key_info->key_length != 21) /* permuted 168-bit key */ &&(key_info->key_length != 24))) { ret = FSL_RETURN_ERROR_S; goto out; } key_length = 21; /* 168-bit PK */ } /* operational block */ { uint8_t key_value[key_length]; #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Creating random key\n"); #endif ret = fsl_shw_get_random(user_ctx, key_length, key_value); if (ret != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("get_random for CREATE KEY failed\n"); #endif goto out; } if (key_info->flags & FSL_SKO_KEY_SW_KEY) { ret = load_slot(user_ctx, key_info, key_value); } else { di_code = dryice_set_programmed_key(key_value, 8 * key_length, 0); if (di_code != 0) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("di set_pk failed: %s\n", di_error_string(di_code)); #endif ret = FSL_RETURN_ERROR_S; goto out; } ret = FSL_RETURN_OK_S; } memset(key_value, 0, key_length); } /* end operational block */ #ifdef DIAG_SECURITY_FUNC if (ret != FSL_RETURN_OK_S) { LOG_DIAG("Loading random key failed"); } #endif out: return ret; } /* end fn create */ static fsl_shw_return_t accept(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, const uint8_t * key) { uint8_t permuted_key[21]; fsl_shw_return_t ret = FSL_RETURN_ERROR_S; if (key == NULL) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("ACCEPT: Red Key is NULL"); #endif ret = FSL_RETURN_ERROR_S; goto out; } #ifdef DIAG_SECURITY_FUNC dump("red", key, key_info->key_length); #endif /* Only SW keys go into the keystore */ if (key_info->flags & FSL_SKO_KEY_SW_KEY) { /* Copy in safe number of bytes of Red key */ ret = load_slot(user_ctx, key_info, key); } else { /* not SW key */ di_return_t di_ret; /* Only 3DES PGM key types can be established */ if (((key_info->pf_key != FSL_SHW_PF_KEY_PRG) && (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG)) || (key_info->algorithm != FSL_KEY_ALG_TDES)) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS ("ACCEPT: Failed trying to establish non-PRG" " or invalid 3DES Key: iim%d, iim_prg%d, alg%d\n", (key_info->pf_key != FSL_SHW_PF_KEY_PRG), (key_info->pf_key != FSL_SHW_PF_KEY_IIM_PRG), (key_info->algorithm != FSL_KEY_ALG_TDES)); #endif ret = FSL_RETURN_ERROR_S; goto out; } if ((key_info->key_length != 16) && (key_info->key_length != 21) && (key_info->key_length != 24)) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("ACCEPT: Failed trying to establish" " invalid 3DES Key: len=%d (%d)\n", key_info->key_length, ((key_info->key_length != 16) && (key_info->key_length != 21) && (key_info->key_length != 24))); #endif ret = FSL_RETURN_BAD_KEY_LENGTH_S; goto out; } /* Convert key into 168-bit value and put it into PK */ if (key_info->key_length != 21) { fsl_shw_permute1_bytes(key, permuted_key, key_info->key_length / 8); di_ret = dryice_set_programmed_key(permuted_key, 168, 0); } else { /* Already permuted ! */ di_ret = dryice_set_programmed_key(key, 168, 0); } if (di_ret != DI_SUCCESS) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS ("ACCEPT: DryIce error setting Program Key: %s", di_error_string(di_ret)); #endif ret = FSL_RETURN_ERROR_S; goto out; } } ret = FSL_RETURN_OK_S; out: memset(permuted_key, 0, 21); return ret; } /* end fn accept */ /*! * 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 by calling #fsl_shw_extract_key() at some later time). * * The protected key will not be available for use until this operation * successfully completes. * * @bug This whole discussion needs review. * * 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 = FSL_RETURN_ERROR_S; unsigned original_key_length = key_info->key_length; unsigned rounded_key_length; unsigned slot_allocated = 0; /* For now, only blocking mode calls are supported */ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("%s: Non-blocking call not supported\n", __FUNCTION__); #endif ret = FSL_RETURN_BAD_FLAG_S; goto out; } /* HW keys are always 'established', but otherwise do not allow user * to establish over the top of an established key. */ if ((key_info->flags & FSL_SKO_KEY_ESTABLISHED) && !(key_info->flags & FSL_SKO_KEY_SELECT_PF_KEY)) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("%s: Key already established\n", __FUNCTION__); #endif ret = FSL_RETURN_BAD_FLAG_S; goto out; } /* @bug VALIDATE KEY flags here -- SW or PRG/IIM_PRG */ /* 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 (LENGTH_PATCH && (original_key_length & LENGTH_PATCH_MASK) != 0) { rounded_key_length = original_key_length + LENGTH_PATCH - (original_key_length & LENGTH_PATCH_MASK); } else { rounded_key_length = original_key_length; } /* SW keys need a place to live */ if (key_info->flags & FSL_SKO_KEY_SW_KEY) { ret = alloc_slot(user_ctx, key_info); if (ret != FSL_RETURN_OK_S) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Slot allocation failed\n"); #endif goto out; } slot_allocated = 1; } switch (establish_type) { case FSL_KEY_WRAP_CREATE: #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Creating random key\n"); #endif ret = create(user_ctx, key_info); break; case FSL_KEY_WRAP_ACCEPT: #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Accepting plaintext key\n"); #endif ret = accept(user_ctx, key_info, key); break; case FSL_KEY_WRAP_UNWRAP: #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Unwrapping wrapped key\n"); #endif ret = unwrap(user_ctx, key_info, key); break; default: ret = FSL_RETURN_BAD_FLAG_S; break; } /* switch */ out: if (ret != FSL_RETURN_OK_S) { if (slot_allocated) { (void)dealloc_slot(user_ctx, key_info); } key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED; } else { key_info->flags |= FSL_SKO_KEY_ESTABLISHED; } return ret; } /* end fn fsl_shw_establish_key */ /*! * 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 a software key (see #fsl_shw_release_key()) * so it must be re-established before reuse. This is not true of PGM keys. * * 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 = FSL_RETURN_ERROR_S; /* For now, only blocking mode calls are supported */ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) { ret = FSL_RETURN_BAD_FLAG_S; goto out; } #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Wrapping a key\n"); #endif if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS("%s: Key not established\n", __FUNCTION__); #endif ret = FSL_RETURN_BAD_FLAG_S; goto out; } /* Verify that a SW key info really belongs to a SW key */ if (key_info->flags & FSL_SKO_KEY_SW_KEY) { /* ret = FSL_RETURN_BAD_FLAG_S; goto out;*/ } ret = wrap(user_ctx, key_info, covered_key); if (ret != FSL_RETURN_OK_S) { goto out; } if (key_info->flags & FSL_SKO_KEY_SW_KEY) { /* Need to deallocate on successful extraction */ (void)dealloc_slot(user_ctx, key_info); /* Mark key not available in the flags */ key_info->flags &= ~(FSL_SKO_KEY_ESTABLISHED | FSL_SKO_KEY_PRESENT); memset(key_info->key, 0, sizeof(key_info->key)); } out: return ret; } /* end fn fsl_shw_extract_key */ /*! * 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 = FSL_RETURN_ERROR_S; /* For now, only blocking mode calls are supported */ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Not in blocking mode\n"); #endif ret = FSL_RETURN_BAD_FLAG_S; goto out; } #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Releasing a key\n"); #endif if (!(key_info->flags & FSL_SKO_KEY_ESTABLISHED)) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Key not established\n"); #endif ret = FSL_RETURN_BAD_FLAG_S; goto out; } if (key_info->flags & FSL_SKO_KEY_SW_KEY) { (void)dealloc_slot(user_ctx, key_info); /* Turn off 'established' flag */ #ifdef DIAG_SECURITY_FUNC LOG_DIAG("dealloc_slot() called\n"); #endif key_info->flags &= ~FSL_SKO_KEY_ESTABLISHED; ret = FSL_RETURN_OK_S; goto out; } if ((key_info->pf_key == FSL_SHW_PF_KEY_PRG) || (key_info->pf_key == FSL_SHW_PF_KEY_IIM_PRG)) { di_return_t di_ret; di_ret = dryice_release_programmed_key(); if (di_ret != DI_SUCCESS) { #ifdef DIAG_SECURITY_FUNC LOG_DIAG_ARGS ("dryice_release_programmed_key() failed: %d\n", di_ret); #endif ret = FSL_RETURN_ERROR_S; goto out; } } else { #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Neither SW nor HW key\n"); #endif ret = FSL_RETURN_BAD_FLAG_S; goto out; } ret = FSL_RETURN_OK_S; out: return ret; } /* end fn fsl_shw_release_key */ fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, uint8_t * key) { fsl_shw_return_t ret = FSL_RETURN_INTERNAL_ERROR_S; /* Only blocking mode calls are supported */ if (!(user_ctx->flags & FSL_UCO_BLOCKING_MODE)) { ret = FSL_RETURN_BAD_FLAG_S; goto out; } printk("Reading a key\n"); #ifdef DIAG_SECURITY_FUNC LOG_DIAG("Reading a key"); #endif if (key_info->flags & FSL_SKO_KEY_PRESENT) { memcpy(key_info->key, key, key_info->key_length); ret = FSL_RETURN_OK_S; } else if (key_info->flags & FSL_SKO_KEY_ESTABLISHED) { printk("key established\n"); if (key_info->keystore == NULL) { printk("keystore is null\n"); /* First verify that the key access is valid */ ret = system_keystore.slot_verify_access(system_keystore. user_data, key_info->userid, key_info-> handle); printk("key in system keystore\n"); /* Key is in system keystore */ ret = keystore_slot_read(&system_keystore, key_info->userid, key_info->handle, key_info->key_length, key); } else { printk("key goes in user keystore.\n"); /* Key goes in user keystore */ ret = keystore_slot_read(key_info->keystore, key_info->userid, key_info->handle, key_info->key_length, key); } } out: return ret; } /* end fn fsl_shw_read_key */ #else /* __KERNEL__ && DRYICE */ /* User mode -- these functions are unsupported */ 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) { (void)user_ctx; (void)key_info; (void)establish_type; (void)key; return FSL_RETURN_NO_RESOURCE_S; } fsl_shw_return_t fsl_shw_extract_key(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, uint8_t * covered_key) { (void)user_ctx; (void)key_info; (void)covered_key; return FSL_RETURN_NO_RESOURCE_S; } fsl_shw_return_t fsl_shw_release_key(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info) { (void)user_ctx; (void)key_info; return FSL_RETURN_NO_RESOURCE_S; } fsl_shw_return_t fsl_shw_read_key(fsl_shw_uco_t * user_ctx, fsl_shw_sko_t * key_info, uint8_t * key) { (void)user_ctx; (void)key_info; (void)key; return FSL_RETURN_NO_RESOURCE_S; } #endif /* __KERNEL__ && DRYICE */