diff options
Diffstat (limited to 'src/s390_crypto.c')
-rw-r--r-- | src/s390_crypto.c | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/src/s390_crypto.c b/src/s390_crypto.c new file mode 100644 index 0000000..2e7a0f5 --- /dev/null +++ b/src/s390_crypto.c @@ -0,0 +1,338 @@ +/* This program is released under the Common Public License V1.0 + * + * You should have received a copy of Common Public License V1.0 along with + * with this program. + */ + +/* + * Entry point for crypto graphic instructions on s390. If a instruction + * is not available, instruction will be simulated in software. + * + * Authors(s): Ralph Wuerthner <rwuerthn@de.ibm.com> + * Jan Glauber <jan.glauber@de.ibm.com> + * Felix Beck <felix.beck@de.ibm.com> + * Christian Maaser <cmaaser@de.ibm.com> + * Holger Dengler <hd@linux.vnet.ibm.com> + * Ingo Tuchscherer <ingo.tuchscherer.linux.vnet.ibm.com> + * + * Copyright IBM Copr. 2007, 2009, 2011, 2013 + */ + +#include <stdint.h> +#include <fcntl.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include "s390_crypto.h" +#include "init.h" + +s390_supported_function_t s390_kimd_functions[] = { + {SHA_1, S390_CRYPTO_SHA_1, &sha1_switch}, + {SHA_224, S390_CRYPTO_SHA_256, &sha256_switch}, + {SHA_256, S390_CRYPTO_SHA_256, &sha256_switch}, + {SHA_384, S390_CRYPTO_SHA_512, &sha512_switch}, + {SHA_512, S390_CRYPTO_SHA_512, &sha512_switch}, + {GHASH, S390_CRYPTO_GHASH, &msa4_switch} +}; + +s390_supported_function_t s390_kmc_functions[] = { + {DEA_ENCRYPT, S390_CRYPTO_DEA_ENCRYPT, &des_switch}, + {DEA_DECRYPT, S390_CRYPTO_DEA_DECRYPT, &des_switch}, + {TDEA_192_ENCRYPT, S390_CRYPTO_TDEA_192_ENCRYPT, &tdes_switch}, + {TDEA_192_DECRYPT, S390_CRYPTO_TDEA_192_DECRYPT, &tdes_switch}, + {AES_128_ENCRYPT, S390_CRYPTO_AES_128_ENCRYPT, &aes128_switch}, + {AES_128_DECRYPT, S390_CRYPTO_AES_128_DECRYPT, &aes128_switch}, + {AES_192_ENCRYPT, S390_CRYPTO_AES_192_ENCRYPT, &aes192_switch}, + {AES_192_DECRYPT, S390_CRYPTO_AES_192_DECRYPT, &aes192_switch}, + {AES_256_ENCRYPT, S390_CRYPTO_AES_256_ENCRYPT, &aes256_switch}, + {AES_256_DECRYPT, S390_CRYPTO_AES_256_DECRYPT, &aes256_switch}, + {AES_128_XTS_ENCRYPT, S390_CRYPTO_AES_128_XTS_ENCRYPT, &msa4_switch}, + {AES_128_XTS_DECRYPT, S390_CRYPTO_AES_128_XTS_DECRYPT, &msa4_switch}, + {AES_256_XTS_ENCRYPT, S390_CRYPTO_AES_256_XTS_ENCRYPT, &msa4_switch}, + {AES_256_XTS_DECRYPT, S390_CRYPTO_AES_256_XTS_DECRYPT, &msa4_switch}, + {PRNG, S390_CRYPTO_PRNG, &prng_switch} +}; + +s390_supported_function_t s390_msa4_functions[] = { + {DEA_ENCRYPT, S390_CRYPTO_DEA_ENCRYPT, &msa4_switch}, + {DEA_DECRYPT, S390_CRYPTO_DEA_DECRYPT, &msa4_switch}, + {TDEA_192_ENCRYPT, S390_CRYPTO_TDEA_192_ENCRYPT, &msa4_switch}, + {TDEA_192_DECRYPT, S390_CRYPTO_TDEA_192_DECRYPT, &msa4_switch}, + {AES_128_ENCRYPT, S390_CRYPTO_AES_128_ENCRYPT, &msa4_switch}, + {AES_128_DECRYPT, S390_CRYPTO_AES_128_DECRYPT, &msa4_switch}, + {AES_192_ENCRYPT, S390_CRYPTO_AES_192_ENCRYPT, &msa4_switch}, + {AES_192_DECRYPT, S390_CRYPTO_AES_192_DECRYPT, &msa4_switch}, + {AES_256_ENCRYPT, S390_CRYPTO_AES_256_ENCRYPT, &msa4_switch}, + {AES_256_DECRYPT, S390_CRYPTO_AES_256_DECRYPT, &msa4_switch}, + {AES_128_XTS_ENCRYPT, S390_CRYPTO_AES_128_XTS_ENCRYPT, &msa4_switch}, + {AES_128_XTS_DECRYPT, S390_CRYPTO_AES_128_XTS_DECRYPT, &msa4_switch}, + {AES_256_XTS_ENCRYPT, S390_CRYPTO_AES_256_XTS_ENCRYPT, &msa4_switch}, + {AES_256_XTS_DECRYPT, S390_CRYPTO_AES_256_XTS_DECRYPT, &msa4_switch} +}; + +int read_cpuinfo(void) +{ + int msa = 0; + FILE *handle = fopen("/proc/cpuinfo", "r"); + if (handle) { + char buffer[80]; + int i = 0; + while(fgets(buffer, sizeof(buffer), handle)) { + i++; + if(strstr(buffer,"features") && strstr(buffer,"msa")) { + msa = 1; + break; + } + } + fclose(handle); + } + return msa; +} + +int read_facility_bits(void) +{ + int msa = 0; + unsigned long long facility_bits[2]; + struct sigaction oldact; + sigset_t oldset; + int rc = -1; + rc = begin_sigill_section(&oldact, &oldset); + if (!rc) { + rc = __stfle(facility_bits, 2); + end_sigill_section(&oldact, &oldset); + } + if (rc == 2) { + // stfle should return 2 + if(facility_bits[0] & (1ULL << (63 - 17))) + msa = 1; + if(facility_bits[1] & (1ULL << (127 - 77))) + msa = 4; + } + return msa; +} + +void set_switches(int msa) +{ + unsigned char mask[16]; + unsigned int n; + unsigned int on = 0; + struct sigaction oldact; + sigset_t oldset; + /* The function arrays contain integers. Thus to compute the amount of + * their elements the result of sizeof(*functions) has to be divided by + * sizeof(int). + * The msa4_switch will be set in the kimd function. Because this is + * the only switch for all MSA4 functions we just set it through the + * kimd query and do not need to over the whole array. Therfore there + * is also no distict setting of the switch needed in form + * msa4_switch = 1. */ + memset(mask, 0, sizeof(mask)); + if (msa) { + if (begin_sigill_section(&oldact, &oldset) == 0) { + s390_kmc(S390_CRYPTO_QUERY, mask, (void *) 0, (void *) 0, 0); + end_sigill_section(&oldact, &oldset); + } + } + for (n = 0; n < (sizeof(s390_kmc_functions) / + sizeof(s390_supported_function_t)); n++) { + if (S390_CRYPTO_TEST_MASK(mask, s390_kmc_functions[n].hw_fc)) + on = 1; + else + on = 0; + *s390_kmc_functions[n].enabled = on; + } + + if (msa) { + if (begin_sigill_section(&oldact, &oldset) == 0) { + s390_kimd(S390_CRYPTO_QUERY, mask, (void *) 0, 0); + end_sigill_section(&oldact, &oldset); + } + } + + for (n = 0; n < (sizeof(s390_kimd_functions) / + sizeof(s390_supported_function_t)); n++) { + if (S390_CRYPTO_TEST_MASK(mask, s390_kimd_functions[n].hw_fc)) + on = 1; + else + on = 0; + *s390_kimd_functions[n].enabled = on; + } +} + +void s390_crypto_switches_init(void) +{ + /* First read cpu info to check if msa feature is available. + * If it is not available, execute stfle instructions to read the + * facility bits. + * If then crypto support is detected crypto functions will be queryed + * from the processor */ + int msa; + msa = read_cpuinfo(); + + msa = read_facility_bits(); + if (!msa) + msa = read_cpuinfo(); + + set_switches(msa); +} + +/* + * The first field represents the mechanism ID. + * The second field represents the function family type (category), + * The third filed represents the function code. + * This function code will be used later to check if HW support + * is available and modifies the SW/HW-support-flag. + * SHW - static hardware support (CPACF) + * DHW - dynamic hardware support (crypto adapter) + * SW - software support + * Bit field flags: [0|0|0|0|0|SHW|DHW|SW] + * The last filed represent the property flags + */ +libica_func_list_element_int icaList[] = { + {SHA1, KIMD, SHA_1 , ICA_FLAG_SW, 0}, + {SHA224, KIMD, SHA_256 , ICA_FLAG_SW, 0}, + {SHA256, KIMD, SHA_256 , ICA_FLAG_SW, 0}, + {SHA384, KIMD, SHA_512 , ICA_FLAG_SW, 0}, + {SHA512, KIMD, SHA_512 , ICA_FLAG_SW, 0}, + {G_HASH, KIMD, GHASH , 0, 0}, + + {DES_ECB, KMC, DEA_ENCRYPT, ICA_FLAG_SW, 0}, + {DES_CBC, KMC, DEA_ENCRYPT, ICA_FLAG_SW, 0}, + {DES_OFB, MSA4, DEA_ENCRYPT, 0, 0}, + {DES_CFB, MSA4, DEA_ENCRYPT, 0, 0}, + {DES_CTR, MSA4, DEA_ENCRYPT, 0, 0}, + {DES_CMAC, MSA4, DEA_ENCRYPT, 0, 0}, // CPACF only (MSA4) + + {DES3_ECB, KMC, TDEA_192_ENCRYPT, ICA_FLAG_SW, 0}, + {DES3_CBC, KMC, TDEA_192_ENCRYPT, ICA_FLAG_SW, 0}, + {DES3_OFB, MSA4, TDEA_192_ENCRYPT, 0, 0}, + {DES3_CFB, MSA4, TDEA_192_ENCRYPT, 0, 0}, + {DES3_CTR, MSA4, TDEA_192_ENCRYPT, 0, 0}, + {DES3_CMAC, MSA4, TDEA_192_ENCRYPT, 0, 0}, + + {AES_ECB, KMC, AES_128_ENCRYPT, ICA_FLAG_SW, 0}, + {AES_CBC, KMC, AES_128_ENCRYPT, ICA_FLAG_SW, 0}, + {AES_OFB, MSA4, AES_128_ENCRYPT, 0, 0}, + {AES_CFB, MSA4, AES_128_ENCRYPT, 0, 0}, + {AES_CTR, MSA4, AES_128_ENCRYPT, 0, 0}, + {AES_CMAC, MSA4, AES_128_ENCRYPT, 0, 0}, + {AES_CCM, MSA4, AES_128_ENCRYPT, 0, 0}, + {AES_GCM, MSA4, AES_128_ENCRYPT, 0, 0}, + {AES_XTS, MSA4, AES_128_XTS_ENCRYPT, 0, 0}, + {P_RNG, ADAPTER, 0, ICA_FLAG_SHW | ICA_FLAG_SW, 0}, // SHW (CPACF) + SW + {RSA_ME, ADAPTER, 0, ICA_FLAG_DHW | ICA_FLAG_SW, 0x0F}, // DHW (CEX) + SW / 512,1024,2048, 4096 bit key length + {RSA_CRT, ADAPTER, 0, ICA_FLAG_DHW | ICA_FLAG_SW, 0x0F}, // DHW (CEX) + SW / 512,1024,2048, 4096 bit key length + {RSA_KEY_GEN_ME, ADAPTER, 0, ICA_FLAG_SW, 0}, // SW (openssl) + {RSA_KEY_GEN_CRT, ADAPTER, 0, ICA_FLAG_SW, 0}, // SW (openssl) + +/* available for the MSA4 instruction */ +/* available for the RSA instruction */ + +}; + +/* + * initializes the libica function list + * Query s390_xxx_functions for each algorithm to check + * CPACF support and update the corresponding SHW-flags. + */ +int s390_initialize_functionlist() { + + unsigned int list_len = (sizeof(icaList)/sizeof(libica_func_list_element_int)), x; + + for (x=0; x<=list_len; x++) { + switch ((int)icaList[x].type) { + case KIMD: + icaList[x].flags = icaList[x].flags | + ((*s390_kimd_functions[icaList[x].id].enabled)? 4: 0); + break; + case KMC: + icaList[x].flags = icaList[x].flags | + ((*s390_kmc_functions[icaList[x].id].enabled)? 4: 0); + if (icaList[x].id == AES_128_ENCRYPT) { // check for the maximum size + if (*s390_kmc_functions[icaList[AES_256_ENCRYPT].id].enabled) + icaList[x].property = icaList[x].property | 4; // 256 bit + if (*s390_kmc_functions[icaList[AES_192_ENCRYPT].id].enabled) + icaList[x].property = icaList[x].property | 2; // 192 bit + if (*s390_kmc_functions[icaList[AES_128_ENCRYPT].id].enabled) + icaList[x].property = icaList[x].property | 1; // 128 bit + } + break; + case MSA4: + icaList[x].flags = icaList[x].flags | + ((*s390_msa4_functions[icaList[x].id].enabled)? 4: 0); + if (icaList[x].id == AES_128_ENCRYPT) { // check for the maximum size + if (*s390_msa4_functions[icaList[AES_256_ENCRYPT].id].enabled) + icaList[x].property = icaList[x].property | 4; // 256 bit + if (*s390_msa4_functions[icaList[AES_192_ENCRYPT].id].enabled) + icaList[x].property = icaList[x].property | 2; // 192 bit + if (*s390_msa4_functions[icaList[AES_128_ENCRYPT].id].enabled) + icaList[x].property = icaList[x].property | 1; // 128 bit + } + else if (icaList[x].id == AES_128_XTS_ENCRYPT) { // check for the maximum size + if (*s390_msa4_functions[icaList[AES_256_XTS_ENCRYPT].id].enabled) + icaList[x].property = icaList[x].property | 2; // 256 bit + if (*s390_msa4_functions[icaList[AES_128_XTS_ENCRYPT].id].enabled) + icaList[x].property = icaList[x].property | 1; // 128 bit + } + break; + default: + break; + } + } + return 0; +} + +/** + * Function that returns a list of crypto mechanisms supported by libica. + * @param pmech_list + * Pointer to an array of libica_func_list_element + * If NULL, the API will return the number of elements to allocate + * in the @pmech_list_len parameter. + * If not NULL, libica will assume @pmech_list is an array that has + * @pmech_list_len elements. + * On success, @pmech_list will be filled out with the supported libica + * crypto mechanisms. + * @param pmech_list_len + * number of list entries + * On input, pointer to the number of elements allocated in the + * @pmech_list array. + * On output, @pmech_list_len will contain the number of items copied to + * the @pmech_list array, or the number of items libica would have returned + * in case the @pmech_list parameter is set to NULL. + * + * @return + * 0 on success + * EINVAL if at least one invalid parameter is given + * + * A typical usage scenario would be that an exploiter makes a first call to + * ica_get_functionlist() with @pmech_list set to NULL in order to determine + * the number of elements to allocate. This is followed by a second call to + * ica_get_functionlist() with a valid pointer @pmech_list to an array of + * libica_func_list_element structures with @pmech_list_len elements. + */ +int s390_get_functionlist(libica_func_list_element *pmech_list, + unsigned int *pmech_list_len) { + int x; + + if (!pmech_list_len) { + return EINVAL; + } + + if (!pmech_list) { + *pmech_list_len = sizeof(icaList)/sizeof(libica_func_list_element_int); + return 0; + } + else if (*pmech_list_len < + (sizeof(icaList)/sizeof(libica_func_list_element_int)) ) { + return EINVAL; + } + + for (x=0; x<*pmech_list_len; x++) { + pmech_list[x].mech_mode_id = icaList[x].mech_mode_id; + pmech_list[x].flags = icaList[x].flags; + pmech_list[x].property = icaList[x].property; + } + + return 0; +} + |