diff options
author | Dimitri John Ledkov <xnox@ubuntu.com> | 2016-11-24 16:48:43 +0000 |
---|---|---|
committer | Dimitri John Ledkov <xnox@ubuntu.com> | 2016-11-24 16:48:43 +0000 |
commit | b33f77cf05cb266494ef8dacd21758271e7103a6 (patch) | |
tree | bf5b172d5a7b9bee3877c4fc838cc3ebf9aa308f /src/s390_rsa.c |
Import libica_3.0.1.orig.tar.gz
[dgit import orig libica_3.0.1.orig.tar.gz]
Diffstat (limited to 'src/s390_rsa.c')
-rw-r--r-- | src/s390_rsa.c | 1037 |
1 files changed, 1037 insertions, 0 deletions
diff --git a/src/s390_rsa.c b/src/s390_rsa.c new file mode 100644 index 0000000..811d935 --- /dev/null +++ b/src/s390_rsa.c @@ -0,0 +1,1037 @@ +/* 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. + */ + +/** + * Authors: Felix Beck <felix.beck@de.ibm.com> + * Christian Maaser <cmaaser@de.ibm.com> + * + * Some parts of the content of this file have been moved from former + * icalinux.c to this file. + * + * Copyright IBM Corp. 2009, 2011 + */ + +#include <string.h> +#include <errno.h> +#include <stdint.h> +#include <openssl/crypto.h> +#include <openssl/rsa.h> +#include <openssl/crypto.h> +#include <pthread.h> +#include <semaphore.h> + +#include <openssl/opensslconf.h> +#ifdef OPENSSL_FIPS +#include <openssl/fips.h> +#endif /* OPENSSL_FIPS */ + +#include "fips.h" +#include "s390_rsa.h" +#include "s390_prng.h" + +static unsigned int mod_expo_sw(int arg_length, char *arg, int exp_length, + char *exp, int mod_length, char *mod, + int *res_length, char *res, BN_CTX *ctx); +static unsigned int mod_mul_sw(int fc_1_length, char *fc1, int fc_2_length, + char *fc2, int mod_length, char *mod, + int *res_length, char *res, BN_CTX *ctx); +static unsigned int mod_sw(int arg_length, char *arg, int mod_length, + char *mod, int *res_length, char *res, BN_CTX *ctx); +static unsigned int add_sw(int aug_length, char *aug, int add_length, + char *add, int *res_length, char *res, BN_CTX *ctx); +static unsigned int mod_sub_sw(int min_length, char *minu, int sub_length, + char *sub, int mod_length, char *mod, + int *res_length, char *res, BN_CTX * ctx); +static unsigned int mul_sw(int fc_1_length, char *fc1, int fc_2_length, + char *fc2, int *res_length, char *res, BN_CTX *ctx); +static unsigned int mod_expo_sw(int arg_length, char *arg, int exp_length, + char *exp, int mod_length, char *mod, + int *res_length, char *res, BN_CTX *ctx); + +struct thread_data +{ + unsigned int mod_bit_length; + unsigned long *pub_exp; + RSA *rsa; +}; + +static void *__rsa_key_generate(void *ptr) +{ + struct thread_data *pth_data; + unsigned int modulus_bit_length; + unsigned long *public_exponent; + + pth_data = (struct thread_data*)ptr; + modulus_bit_length = pth_data->mod_bit_length; + public_exponent = pth_data->pub_exp; + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return NULL; +#endif /* ICA_FIPS */ + + if (*public_exponent == 0) + { + do { + if (s390_prng((unsigned char*)public_exponent, + sizeof(unsigned long)) != 0) + return NULL; + } while (*public_exponent <= 2 || !(*public_exponent % 2)); + } + + RSA *rsa = RSA_new(); + BIGNUM *exp = BN_new(); + if (!exp || !rsa) { + RSA_free(rsa); + BN_free(exp); + return NULL; + } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + BN_GENCB *cb = BN_GENCB_new(); + if(!cb) { + RSA_free(rsa); + BN_free(exp); + return NULL; + } +#else + BN_GENCB dummy; + BN_GENCB *cb = &dummy; +#endif /* OPENSSL_VERSION_NUMBER */ + + BN_set_word(exp, *public_exponent); + BN_GENCB_set_old(cb, NULL, NULL); + + if (RSA_generate_key_ex(rsa, modulus_bit_length, exp, cb) == 0) { + RSA_free(rsa); + rsa = NULL; + pth_data->rsa = NULL; + } + + BN_free(exp); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + BN_GENCB_free(cb); +#endif /* OPENSSL_VERSION_NUMBER */ + pth_data->rsa = rsa; + return 0; +} + + +RSA* rsa_key_generate(unsigned int modulus_bit_length, + unsigned long *public_exponent) +{ + pthread_t tid; + struct thread_data th_data; + int rc; + + sem_wait(&openssl_crypto_lock_mtx); + + th_data.mod_bit_length = modulus_bit_length; + th_data.pub_exp = public_exponent; + rc = pthread_create(&(tid), NULL, (void *)&__rsa_key_generate, + (void *)(&th_data)); + if (rc) + return 0; + rc = pthread_join(tid, NULL); + + if (!rc && th_data.rsa) { + sem_post(&openssl_crypto_lock_mtx); + return th_data.rsa; + } + sem_post(&openssl_crypto_lock_mtx); + return NULL; +} + +/** + * @brief Create a RSA modulus/expo key pair + * + * This function generates and returns a public/private key pair in + * modulus/exponent format. A completion code is returned to indicate + * success/failure. + * @param device_handle + * Previously opened device handle. + * @param modulus_bit_length + * Bit length of modulus to be generated. + * @param public_key + * Buffer for the public key. On output contains the public key. + * @param private_key + * Buffer of the private key. On output contains the private key. + * + * Returns 0 if successful. + */ +unsigned int rsa_key_generate_mod_expo(ica_adapter_handle_t deviceHandle, + unsigned int modulus_bit_length, + ica_rsa_key_mod_expo_t *public_key, + ica_rsa_key_mod_expo_t *private_key) +{ +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + RSA *rsa = rsa_key_generate(modulus_bit_length, + (unsigned long*)(public_key->exponent + + public_key->key_length - + sizeof(unsigned long))); + if (!rsa) + return errno; + + const BIGNUM *n, *d; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + RSA_get0_key(rsa, &n, NULL, &d); +#else + n = rsa->n; + d = rsa->d; +#endif /* OPENSSL_VERSION_NUMBER */ + + /* Set key buffers zero to make sure there is no + * unneeded junk in between. + */ + memset(public_key->modulus, 0, public_key->key_length); + memset(private_key->modulus, 0, private_key->key_length); + memset(private_key->exponent, 0, private_key->key_length); + + unsigned int bn_length = BN_num_bytes(n); + unsigned int offset = 0; + + if (bn_length < public_key->key_length) + offset = public_key->key_length - bn_length; + else + offset = 0; + BN_bn2bin(n, public_key->modulus + offset); + + memcpy(private_key->modulus, public_key->modulus, + public_key->key_length); + + bn_length = BN_num_bytes(d); + if (bn_length < private_key->key_length) + offset = private_key->key_length - bn_length; + else + offset = 0; + BN_bn2bin(d, private_key->exponent + offset); + + RSA_free(rsa); + + return 0; +} + +/** + * This function generates and returns a public/private key pair in CRT format. + * + * @param device_handle + * Previously opened device handle. + * @param modulus_bit_length + * Bit length of modulus to be generated. + * @param public_key + * Buffer for the public key. On output contains the public key. + * @param private_key + * Buffer of the private key. On output contains the private key. + * + * Returns 0 if successful. + */ +unsigned int rsa_key_generate_crt(ica_adapter_handle_t deviceHandle, + unsigned int modulus_bit_length, + ica_rsa_key_mod_expo_t *public_key, + ica_rsa_key_crt_t *private_key) +{ +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + RSA *rsa = rsa_key_generate(modulus_bit_length, + (unsigned long*)(public_key->exponent + + public_key->key_length - + sizeof(unsigned long))); + if (!rsa) + return errno; + + const BIGNUM *n, *p, *q, *dmp1, *dmq1, *iqmp; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + RSA_get0_key(rsa, &n, NULL, NULL); + RSA_get0_factors(rsa, &p, &q); + RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp); +#else + n = rsa->n; + p = rsa->p; + q = rsa->q; + dmp1 = rsa->dmp1; + dmq1 = rsa->dmq1; + iqmp = rsa->iqmp; +#endif /* OPENSSL_VERSION_NUMBER */ + + /* Public exponent has already been set, no need to do this here. + * For public key, only modulus needs to be set. + */ + memset(public_key->modulus, 0, public_key->key_length); + + /* Make sure that key parts are copied to the end of the buffer */ + unsigned int offset = 0; + + unsigned int bn_length = BN_num_bytes(n); + if (bn_length < public_key->key_length) + offset = public_key->key_length - bn_length; + else + offset = 0; + BN_bn2bin(n, public_key->modulus + offset); + + memset(private_key->p, 0, (private_key->key_length+1) / 2 + 8); + memset(private_key->q, 0, (private_key->key_length+1) / 2); + memset(private_key->dp, 0, (private_key->key_length+1) / 2 + 8); + memset(private_key->dq, 0, (private_key->key_length+1) / 2); + memset(private_key->qInverse, 0, (private_key->key_length+1) / 2 + 8); + + unsigned int key_part_length = (private_key->key_length+1) / 2; + + /* We add the "+8" because it is a requirement by the crypto adapters + * to have an 8 byte zero pad in the beginning of the fields for: + * p, dp, and qInverse. + */ + + /* Copy p into buffer */ + bn_length = BN_num_bytes(p); + if(bn_length < key_part_length) + offset = key_part_length - bn_length; + else + offset = 0; + BN_bn2bin(p, private_key->p + 8 + offset); + + /* Copy q into buffer */ + bn_length = BN_num_bytes(q); + if(bn_length < key_part_length) + offset = key_part_length - bn_length; + else + offset = 0; + BN_bn2bin(q, private_key->q + offset); + + /* Copy dp into buffer */ + bn_length = BN_num_bytes(dmp1); + if(bn_length < key_part_length) + offset = key_part_length - bn_length; + else + offset = 0; + BN_bn2bin(dmp1, private_key->dp + 8 + offset); + + /* Copy dq into buffer */ + bn_length = BN_num_bytes(dmq1); + if(bn_length < key_part_length) + offset = key_part_length - bn_length; + else + offset = 0; + BN_bn2bin(dmq1, private_key->dq + offset); + + /* Copy qInverse into buffer */ + bn_length = BN_num_bytes(iqmp); + if(bn_length < key_part_length) + offset = key_part_length - bn_length; + else + offset = 0; + + BN_bn2bin(iqmp, private_key->qInverse + 8 + offset); + + RSA_free(rsa); + + return 0; +} +/** + * @deprecated Perform a modular muliplication operation in software. + */ +unsigned int rsa_mod_mult_sw(ica_rsa_modmult_t *pMul) +{ + int rc = 0; + BN_CTX *ctx = NULL; + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + if ((ctx = BN_CTX_new()) == NULL) { + return errno; + } + + rc = mod_mul_sw(pMul->inputdatalength, pMul->inputdata, + pMul->inputdatalength, pMul->b_key, + pMul->inputdatalength, pMul->n_modulus, + (int *)&(pMul->outputdatalength), + pMul->outputdata, ctx); + BN_CTX_free(ctx); + if (rc) + rc = EIO; + return rc; +} + +/** + * Perform a multiprecision modular multiplication using a multiplicand, + * multiplier and modulus. + */ +static unsigned int mod_mul_sw(int fc_1_length, char *fc1, int fc_2_length, + char *fc2, int mod_length, char *mod, + int *res_length, char *res, BN_CTX *ctx) +{ + int rc = 0; + int ln = 0; + int pad = 0; + BIGNUM *b_fc1 = NULL; + BIGNUM *b_fc2 = NULL; + BIGNUM *b_mod = NULL; + BIGNUM *b_res = NULL; + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + BN_CTX_start(ctx); + + b_fc1 = BN_CTX_get(ctx); + b_fc2 = BN_CTX_get(ctx); + b_mod = BN_CTX_get(ctx); + if ((b_res = BN_CTX_get(ctx)) == NULL) { + rc = ENOMEM; + goto cleanup; + } + + b_fc1 = BN_bin2bn((const unsigned char *)fc1, fc_1_length, b_fc1); + b_fc2 = BN_bin2bn((const unsigned char *)fc2, fc_2_length, b_fc2); + b_mod = BN_bin2bn((const unsigned char *)mod, mod_length, b_mod); + + if (!(BN_mod_mul(b_res, b_fc1, b_fc2, b_mod, ctx))) { + goto err; + } + + if ((ln = BN_num_bytes(b_res)) > *res_length) { + rc = EIO; + goto cleanup; + } + + if (ln) + pad = *res_length - ln; + + ln = BN_bn2bin(b_res,(unsigned char *)(res + pad)); + + if (pad) + memset(res, 0, pad); + + goto cleanup; + + err: + rc = EIO; + + cleanup: + BN_CTX_end(ctx); + + return rc; +} + +/** + * Perform a mod expo operation using a key in modulus/exponent form, in + * software. + * @param pMex + * Address of an ica_rsa_modexpo_t, containing: + * input_length - The byte length of the input data + * input_data - Pointer to input data + * b_key - Pointer to the exponent + * n_modulus - Pointer to the modulus + * output_length - On input it contains the byte length of the output + * buffer. On output it contains the actual byte + * length of the output_data + * output_data - Pointer to the output buffer + * + * Returns 0 if successful. + */ +unsigned int rsa_mod_expo_sw(ica_rsa_modexpo_t *pMex) +{ + int rc = 0; + BN_CTX *ctx = NULL; + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + if ((ctx = BN_CTX_new()) == NULL) { + return errno; + } + + /* check if modulus value > data value */ + if ((memcmp(pMex->n_modulus, pMex->inputdata, + pMex->inputdatalength)) <= 0) + return EINVAL; + + rc = mod_expo_sw(pMex->inputdatalength, pMex->inputdata, + pMex->inputdatalength, pMex->b_key, + pMex->inputdatalength, pMex->n_modulus, + (int *)&(pMex->outputdatalength), pMex->outputdata, ctx); + + BN_CTX_free(ctx); + if (rc == 1) + rc = EIO; + return rc; +} + +/** + * Perform a mod expo operation using a key in modulus/exponent form, in + * software. + * @param arg_length + * The byte length of the input data + * @param arg + * Pointer to input data + * @param exp_length + * The byte length of the exponent + * @param exp + * Pointer to the exponent + * @param mod_length + * The byte length of the modulus + * @param mod + * Pointer to the modulus + * @param res_length + * On input it points to the byte length of the output buffer. On output it + * points to the actual byte length of the output_data + * @param res + * Pointer to the output buffer + * @param ctx + * Pointer to a BN_CTX + * + * Returns 0 if successful BN error code if unsuccessful. + */ +static unsigned int mod_expo_sw(int arg_length, char *arg, int exp_length, + char *exp, int mod_length, char *mod, + int *res_length, char *res, BN_CTX *ctx) +{ + int rc = 0; + int ln = 0; + int pad = 0; + BIGNUM *b_arg = NULL; + BIGNUM *b_exp = NULL; + BIGNUM *b_mod = NULL; + BIGNUM *b_res = NULL; + BN_CTX *mod_expo_ctx = NULL; + int mod_expo_rc = 1; + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + BN_CTX_start(ctx); + + b_arg = BN_CTX_get(ctx); + b_exp = BN_CTX_get(ctx); + b_mod = BN_CTX_get(ctx); + if ((b_res = BN_CTX_get(ctx)) == NULL) { + rc = ENOMEM; + goto cleanup; + } + + b_arg = BN_bin2bn((const unsigned char *)arg, arg_length, b_arg); + b_exp = BN_bin2bn((const unsigned char *)exp, exp_length, b_exp); + b_mod = BN_bin2bn((const unsigned char *)mod, mod_length, b_mod); + + // Evidently BN_mod_exp gets a *lot* of temp BN's, so it + // needs a context all its own. + if ((mod_expo_ctx = BN_CTX_new()) == NULL) { + goto err; + } + + mod_expo_rc = BN_mod_exp(b_res, b_arg, b_exp, b_mod, mod_expo_ctx); + BN_CTX_free(mod_expo_ctx); + + if (!(mod_expo_rc)) { + goto err; + } + + if ((ln = BN_num_bytes(b_res)) > *res_length) { + rc = 1; + goto cleanup; + } + + if (ln) + pad = *res_length - ln; + + ln = BN_bn2bin(b_res, (unsigned char *)(res + pad)); + + if (pad) + memset(res, 0, pad); + + goto cleanup; + + err: + rc = EIO; + + cleanup: + BN_CTX_end(ctx); + + return rc; +} + +/** + * Perform a RSA mod expo on input data using a key in CRT format, in software. + * + * @param pCrt + * Address of an ica_rsa_modexpo_crt_t, containing: + * input_length: The byte length of the input data. + * input_data: Pointer to input data b + * output_length: On input it contains the byte length of the output + * buffer. On output it contains the actual byte length + * of the output_data + * output_data: Pointer to the output buffer + * bp_key: Pointer to dp + * bq_key: Pointer to dq + * np_prime: Pointer to p + * nq_prime: Pointer to q + * u_mult_inv: Pointer to u + * + * Returns 0 if successful + */ +unsigned int rsa_crt_sw(ica_rsa_modexpo_crt_t * pCrt) +{ + int rc = 0; + int long_length = 0; + int short_length = 0; + BN_CTX *ctx = NULL; + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + short_length = (pCrt->inputdatalength+1) / 2; + long_length = short_length + 8; +/* + Use variable buffer length. Earlier version contained fixed 136byte + size for ir buffers. Thus the software fallback should be able to + handle keys of bigger size, too. +*/ + char ir1[long_length]; + int ir_1_length = sizeof(ir1); + char ir2[long_length]; + int ir_2_length = sizeof(ir2); + char temp[long_length]; + int temp_length = sizeof(temp); + + if ((ctx = BN_CTX_new()) == NULL) { + return errno; + } + + memset(ir1, 0, sizeof(ir1)); + if ((rc = mod_sw(pCrt->inputdatalength, pCrt->inputdata, + long_length, pCrt->np_prime, &ir_1_length, ir1, ctx)) != 0) + goto err; + + memset(temp, 0, sizeof(temp)); + if ((rc = mod_expo_sw(ir_1_length, ir1, + long_length, pCrt->bp_key, + long_length, pCrt->np_prime, + &temp_length, temp, ctx)) != 0) + goto err; + + memset(ir1, 0, sizeof(ir1)); + memcpy(ir1, temp, temp_length); + ir_1_length = temp_length; + + memset(ir2, 0, sizeof(ir2)); + if ((rc = mod_sw(pCrt->inputdatalength, pCrt->inputdata, + short_length, pCrt->nq_prime, &ir_2_length, + ir2, ctx)) != 0) + goto err; + + temp_length = sizeof(temp); + memset(temp, 0, sizeof(temp)); + if ((rc = mod_expo_sw(ir_2_length, ir2, + short_length, pCrt->bq_key, + short_length, pCrt->nq_prime, + &temp_length, temp, ctx)) != 0) + goto err; + + memset(ir2, 0, sizeof(ir2)); + memcpy(ir2, temp, temp_length); + ir_2_length = temp_length; + + temp_length = sizeof(ir1); + if ((rc = mod_sub_sw(ir_1_length, ir1, + ir_2_length, ir2, + long_length, pCrt->np_prime, + &temp_length, ir1, ctx)) != 0) { + if (rc != -1) { + goto err; + } else { + if (ir_2_length > pCrt->outputdatalength) { + memcpy(pCrt->outputdata, + ir2 + (ir_2_length - + pCrt->outputdatalength), + pCrt->outputdatalength); + } else { + if (ir_2_length < pCrt->outputdatalength) { + memset(pCrt->outputdata, 0, + (pCrt->outputdatalength - + ir_2_length)); + memcpy(pCrt->outputdata + + (pCrt->outputdatalength - + ir_2_length), ir2, ir_2_length); + } else { + memcpy(pCrt->outputdata, ir2, + ir_2_length); + } + } + rc = 0; + goto cleanup; + } + } + + ir_1_length = temp_length; + + temp_length = sizeof(temp); + memset(temp, 0, sizeof(temp)); + if ((rc = mod_mul_sw(ir_1_length, ir1, + long_length, pCrt->u_mult_inv, + long_length, pCrt->np_prime, + &temp_length, temp, ctx)) != 0) + goto err; + + if ((rc = mul_sw(temp_length, temp, + short_length, pCrt->nq_prime, + (int *)&(pCrt->outputdatalength), pCrt->outputdata, ctx)) != 0) + goto err; + + if ((rc = add_sw(pCrt->outputdatalength, pCrt->outputdata, + ir_2_length, ir2, + (int *)&(pCrt->outputdatalength), pCrt->outputdata, ctx)) != 0) + goto err; + + goto cleanup; + + err: + rc = EIO; + + cleanup: + BN_CTX_free(ctx); + + return rc; +} + +/** + * Perform a 'residue modulo' operation using an argument and a modulus. + * @param arg_length The byte length of the input data + * @param arg Pointer to input data + * @param mod_length The byte length of the modulus + * @param mod Pointer to the modulus + * @param res_length + * On input it points to the byte length of the output buffer. On output it + * points to the actual byte length of the output_data. + * @param res Pointer to the output buffer + * @param ctx Pointer to a BN_CTX + * + * Returns 0 if successful, BN error code if unsuccessful + */ +static unsigned int mod_sw(int arg_length, char *arg, int mod_length, + char *mod, int *res_length, char *res, BN_CTX *ctx) +{ + int rc = 0; + int ln = 0; + int pad = 0; + BIGNUM *b_arg = NULL; + BIGNUM *b_mod = NULL; + BIGNUM *b_res = NULL; + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + BN_CTX_start(ctx); + + b_arg = BN_CTX_get(ctx); + b_mod = BN_CTX_get(ctx); + if ((b_res = BN_CTX_get(ctx)) == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + b_arg = BN_bin2bn((const unsigned char *)arg, arg_length, b_arg); + b_mod = BN_bin2bn((const unsigned char *)mod, mod_length, b_mod); + + if (!(BN_mod(b_res, b_arg, b_mod, ctx))) { + goto err; + } + + if ((ln = BN_num_bytes(b_res)) > *res_length) { + rc = 1; + goto cleanup; + } + + if (ln) + pad = *res_length - ln; + + ln = BN_bn2bin(b_res, (unsigned char *)(res + pad)); + + if (pad) + memset(res, 0, pad); + + goto cleanup; + + err: + rc = EIO; + + cleanup: + BN_CTX_end(ctx); + + return rc; +} + +/** + * Perform a multiprecision subtraction modulo a modulus using a minuend, + * subtrahend and modulus + * + * @param min_length The byte length of the minuend + * @param min Pointer to minuend + * @param sub_length The byte length of the subtrahend + * @param sub Pointer to the subtrahend + * @param mod_length The byte length of the modulus + * @param mod The modulus + * @param res_length + * On input it points to the byte length of the output buffer. On output it + * points to the actual byte length of the output_data + * @param res Pointer to the output buffer + * @param ctx Pointer to a BN_CTX + * + * Returns 0 if successful, BN error code if unsuccessful + * Process: + * 1) If the subtrahend exceeds the minuend, use add_sw to + * add the modulus to the minuend + * 2) Call BN_CTX_get for the minuend, subtrahend & result BN's + * 3) Convert the minuend and subtrahend BN's using BN_bin2bn + * 4) Call BN_sub + * 5) Convert the result from a BN to a string using BN_bn2bin + * 6) Call BN_free for the minuend, subtrahend and result BN's + */ +static unsigned int mod_sub_sw(int min_length, char *minu, int sub_length, + char *sub, int mod_length, char *mod, + int *res_length, char *res, BN_CTX * ctx) +{ + int rc = 0; + int ln = 0; + int pad = 0; + + int min_size, sub_size, dif_size; + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + BIGNUM *b_min = NULL; + BIGNUM *b_sub = NULL; + BIGNUM *b_mod = NULL; + BIGNUM *b_res = NULL; + + BN_CTX_start(ctx); + + b_min = BN_CTX_get(ctx); + b_sub = BN_CTX_get(ctx); + b_mod = BN_CTX_get(ctx); + if ((b_res = BN_CTX_get(ctx)) == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + b_min = BN_bin2bn((const unsigned char *)minu, min_length, b_min); + b_sub = BN_bin2bn((const unsigned char *)sub, sub_length, b_sub); + b_mod = BN_bin2bn((const unsigned char *)mod, mod_length, b_mod); + + min_size = BN_num_bytes(b_min); + sub_size = BN_num_bytes(b_sub); + + /* if sub == min, the result is zero, but it's an error */ + if (sub_size == min_size) { + dif_size = memcmp(sub, minu, sub_length); + if (dif_size == 0) { + memset(res, 0, *res_length); + rc = -1; + goto cleanup; + } + } + /* if sub < min, the result is just min - sub */ + if ((sub_size < min_size) || ((sub_size == min_size) && (dif_size < 0))) { + if (!(BN_sub(b_res, b_min, b_sub))) { + goto err; + } + } else { /* sub > min, so the result is (min + mod) - sub */ + if (!(BN_add(b_res, b_min, b_mod))) { + goto err; + } + if (!(BN_sub(b_res, b_res, b_sub))) { + goto err; + } + } + + if ((ln = BN_num_bytes(b_res)) > *res_length) { + rc = 1; + goto cleanup; + } + + if (ln) + pad = *res_length - ln; + + ln = BN_bn2bin(b_res, (unsigned char *)(res + pad)); + + if (pad) + memset(res, 0, pad); + + goto cleanup; + + err: + rc = EIO; + + cleanup: + BN_CTX_end(ctx); + + return rc; +} + +/** + * Perform a multiprecision addition using an augend and addend + * @param aug_length The byte length of the augend + * @param aug Pointer to augend + * @param add_length The byte length of the addend + * @param add Pointer to the addend + * @param res_length On input it points to the byte length of the output buffer. + * On output it points to the actual byte length of the + * output_data + * @param res Pointer to the output buffer + * @param ctx Pointer to a BN_CTX + * + * Returns 0 if successful, BN error code if unsuccessful + * Process: + * 1) Call BN_CTX_get for the augend, addend and result BN's + * 2) Convert the augend and addend BN's using BN_bin2bn + * 3) Call BN_add + * 4) Convert the result from a BN to a string using BN_bn2bin + * 5) Call BN_free for the augend, addend and result BN's +*/ +static unsigned int add_sw(int aug_length, char *aug, int add_length, + char *add, int *res_length, char *res, BN_CTX *ctx) +{ + int rc = 0; + int ln = 0; + int pad = 0; + BIGNUM *b_aug = NULL; + BIGNUM *b_add = NULL; + BIGNUM *b_res = NULL; + + BN_CTX_start(ctx); + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + b_aug = BN_CTX_get(ctx); + b_add = BN_CTX_get(ctx); + if ((b_res = BN_CTX_get(ctx)) == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + b_aug = BN_bin2bn((const unsigned char *)aug, aug_length, b_aug); + b_add = BN_bin2bn((const unsigned char *)add, add_length, b_add); + + if (!(BN_add(b_res, b_aug, b_add))) { + goto err; + } + + if ((ln = BN_num_bytes(b_res)) > *res_length) { + rc = 1; + goto cleanup; + } + + if (ln) + pad = *res_length - ln; + + ln = BN_bn2bin(b_res, (unsigned char *)(res + pad)); + + if (pad) + memset(res, 0, pad); + + goto cleanup; + + err: + rc = EIO; + + cleanup: + BN_CTX_end(ctx); + + return rc; +} + +/** + * Perform a multiprecision multiply using a multiplicand and multiplier. + * @param fc_1_length The byte length of the multiplicand + * @param fc1 Pointer to multiplicand + * @param fc_2_length The byte length of the multiplier + * @param fc2 Pointer to the multiplier + * @param res_length + * On input it points to the byte length of the output buffer. On output it + * points to the actual byte length of the output_data. + * @param res Pointer to the output buffer + * @param ctx Pointer to a BN_CTX + * + * Returns 0 if successful, BN error code if unsuccessful + */ +static unsigned int mul_sw(int fc_1_length, char *fc1, int fc_2_length, + char *fc2, int *res_length, char *res, BN_CTX *ctx) +{ + int rc = 0; + int ln = 0; + int pad = 0; + BIGNUM *b_fc1 = NULL; + BIGNUM *b_fc2 = NULL; + BIGNUM *b_res = NULL; + + BN_CTX_start(ctx); + +#ifdef ICA_FIPS + if ((fips & ICA_FIPS_MODE) && (!FIPS_mode())) + return EACCES; +#endif /* ICA_FIPS */ + + b_fc1 = BN_CTX_get(ctx); + b_fc2 = BN_CTX_get(ctx); + if ((b_res = BN_CTX_get(ctx)) == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + b_fc1 = BN_bin2bn((const unsigned char *)fc1, fc_1_length, b_fc1); + b_fc2 = BN_bin2bn((const unsigned char *)fc2, fc_2_length, b_fc2); + + if (!(BN_mul(b_res, b_fc1, b_fc2, ctx))) { + goto err; + } + + if ((ln = BN_num_bytes(b_res)) > *res_length) { + rc = 1; + goto cleanup; + } + + if (ln) + pad = *res_length - ln; + + ln = BN_bn2bin(b_res, (unsigned char *)(res + pad)); + + if (pad) + memset(res, 0, pad); + + goto cleanup; + + err: + rc = EIO; + + cleanup: + BN_CTX_end(ctx); + + return rc; +} + |