/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ /* SPDX-License-Identifier: Unlicense */ #include "tomcrypt_private.h" /** @file rsa_key.c Free an RSA key, Tom St Denis Basic operations on an RSA key, Steffen Jaeckel */ #ifdef LTC_MRSA static void s_mpi_shrink_multi(void **a, ...) { void **cur; unsigned n; int err; va_list args; void *tmp[10] = { 0 }; void **arg[10] = { 0 }; /* We re-allocate in the order that we received the varargs */ n = 0; err = CRYPT_ERROR; cur = a; va_start(args, a); while (cur != NULL) { if (n >= LTC_ARRAY_SIZE(tmp)) { goto out; } if (*cur != NULL) { arg[n] = cur; if ((err = ltc_mp_init_copy(&tmp[n], *arg[n])) != CRYPT_OK) { goto out; } n++; } cur = va_arg(args, void**); } va_end(args); /* but we clear the old values in the reverse order */ while (n != 0 && arg[--n] != NULL) { ltc_mp_clear(*arg[n]); *arg[n] = tmp[n]; } out: va_end(args); /* clean-up after an error * or after this was called with too many args */ if ((err != CRYPT_OK) || (n >= LTC_ARRAY_SIZE(tmp))) { for (n = 0; n < LTC_ARRAY_SIZE(tmp); ++n) { if (tmp[n] != NULL) { ltc_mp_clear(tmp[n]); } } } } /** This shrinks the allocated memory of a RSA key It will use up some more memory temporarily, but then it will free-up the entire sequence that was once allocated when the key was created/populated. This only works with libtommath >= 1.2.0 in earlier versions it has the inverse effect due to the way it worked internally. Also works for GNU MP, tomsfastmath naturally shows no effect. @param key The RSA key to shrink */ void rsa_shrink_key(rsa_key *key) { LTC_ARGCHKVD(key != NULL); s_mpi_shrink_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, NULL); } /** Init an RSA key @param key The RSA key to initialize @return CRYPT_OK if successful */ int rsa_init(rsa_key *key) { LTC_ARGCHK(key != NULL); key->pss_oaep = 0; XMEMSET(&key->params, 0, sizeof(key->params)); return ltc_mp_init_multi(&key->e, &key->d, &key->N, &key->dQ, &key->dP, &key->qP, &key->p, &key->q, LTC_NULL); } /** Free an RSA key from memory @param key The RSA key to free */ void rsa_free(rsa_key *key) { LTC_ARGCHKVD(key != NULL); ltc_mp_cleanup_multi(&key->q, &key->p, &key->qP, &key->dP, &key->dQ, &key->N, &key->d, &key->e, LTC_NULL); key->pss_oaep = 0; XMEMSET(&key->params, 0, sizeof(key->params)); } static LTC_INLINE int s_rsa_key_valid_rsa_params(ltc_rsa_op_checked *check) { const ltc_rsa_parameters *key_params; /* This is called from PKCS#1 de-/encoder code, so we can't check the key */ if (check->key == NULL) { return CRYPT_OK; } key_params = &check->key->params; /* Key has no PSS/OAEP constraints */ if (!check->key->pss_oaep) { return CRYPT_OK; } /* Key is constrained - operation must use matching PSS/OAEP params */ if (check->params->padding != LTC_PKCS_1_PSS && check->params->padding != LTC_PKCS_1_OAEP) { return CRYPT_PK_TYPE_MISMATCH; } if (key_params->hash_idx != check->hash_alg) { return CRYPT_INVALID_HASH; } if (key_params->mgf1_hash_idx != check->mgf1_hash_alg) { return CRYPT_INVALID_HASH; } return CRYPT_OK; } static LTC_INLINE int s_rsa_key_set_hash_algs(ltc_rsa_op_checked *check) { ltc_rsa_op_parameters *params = check->params; if (hash_is_valid(params->params.hash_idx) != CRYPT_OK) { return CRYPT_INVALID_HASH; } check->hash_alg = params->params.hash_idx; if (params->params.mgf1_hash_idx == -1) { if (params->padding != LTC_PKCS_1_PSS && params->padding != LTC_PKCS_1_OAEP) return CRYPT_OK; } else if (hash_is_valid(params->params.mgf1_hash_idx) == CRYPT_OK) { check->mgf1_hash_alg = params->params.mgf1_hash_idx; return CRYPT_OK; } return CRYPT_INVALID_HASH; } static LTC_INLINE int s_rsa_key_valid_sign(ltc_rsa_op_checked *check) { ltc_rsa_op_parameters *params = check->params; if ((params->padding != LTC_PKCS_1_V1_5) && (params->padding != LTC_PKCS_1_PSS) && (params->padding != LTC_PKCS_1_V1_5_NA1)) { return CRYPT_PK_INVALID_PADDING; } if (params->padding != LTC_PKCS_1_V1_5_NA1) { int err = s_rsa_key_set_hash_algs(check); if (err != CRYPT_OK) { return err; } } if (params->padding == LTC_PKCS_1_V1_5) { /* not all hashes have OIDs... so sad */ if (check->hash_alg == -1 || hash_descriptor[check->hash_alg].OIDlen == 0) { return CRYPT_INVALID_ARG; } } return s_rsa_key_valid_rsa_params(check); } static LTC_INLINE int s_rsa_key_valid_crypt(ltc_rsa_op_checked *check) { ltc_rsa_op_parameters *params = check->params; if ((params->padding != LTC_PKCS_1_V1_5) && (params->padding != LTC_PKCS_1_OAEP)) { return CRYPT_PK_INVALID_PADDING; } if (params->padding == LTC_PKCS_1_OAEP) { int err = s_rsa_key_set_hash_algs(check); if (err != CRYPT_OK) { return err; } } return s_rsa_key_valid_rsa_params(check); } static LTC_INLINE int s_rsa_check_prng(ltc_rsa_op op, ltc_rsa_op_parameters *params) { /* Only PSS signing needs a PRNG, v1.5 signing is deterministic. * All encryption needs a PRNG (OAEP seed, v1.5 EME random padding). */ if ((op & LTC_RSA_OP_SIGN) == LTC_RSA_OP_SIGN && params->padding != LTC_PKCS_1_PSS) return CRYPT_OK; if (params->prng == NULL) return CRYPT_INVALID_PRNG; /* valid prng ? */ return prng_is_valid(params->wprng); } int rsa_key_valid_op(ltc_rsa_op op, ltc_rsa_op_checked *check) { int err; check->hash_alg = check->mgf1_hash_alg = -1; LTC_ARGCHK(check->params != NULL); if ((op & LTC_RSA_OP_PKCS1) != LTC_RSA_OP_PKCS1) { /* PKCS#1 ops don't need an RSA key */ LTC_ARGCHK(check->key != NULL); } if ((op & LTC_RSA_OP_SEND) == LTC_RSA_OP_SEND) { if ((err = s_rsa_check_prng(op, check->params)) != CRYPT_OK) { return err; } } switch (op) { case LTC_RSA_ENCRYPT: case LTC_RSA_DECRYPT: case LTC_PKCS1_ENCRYPT: case LTC_PKCS1_DECRYPT: return s_rsa_key_valid_crypt(check); case LTC_RSA_SIGN: case LTC_RSA_VERIFY: case LTC_PKCS1_SIGN: case LTC_PKCS1_VERIFY: return s_rsa_key_valid_sign(check); } return CRYPT_ERROR; } int rsa_params_equal(const ltc_rsa_parameters *a, const ltc_rsa_parameters *b) { if (a->saltlen != b->saltlen) return 0; if (a->hash_idx != b->hash_idx) return 0; if (a->mgf1_hash_idx != b->mgf1_hash_idx) return 0; return 1; } #endif