projects/linux/crypto/rsa_openssl.c (477 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <openssl/bn.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/rsa.h> #include <openssl/x509.h> #include <stdlib.h> #include <string.h> #include "openssl_check.h" #include "platform_api.h" #include "rsa_openssl.h" #include "common/unused.h" #if OPENSSL_IS_VERSION_3 #include <openssl/param_build.h> #endif #ifdef RSA_ENABLE_PRIVATE_KEY int rsa_openssl_generate_key (const struct rsa_engine *engine, struct rsa_private_key *key, int bits) { EVP_PKEY *rsa = NULL; EVP_PKEY_CTX *ctx = NULL; int status; if ((engine == NULL) || (key == NULL)) { return RSA_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); ctx = EVP_PKEY_CTX_new_id (EVP_PKEY_RSA, NULL); if (ctx == NULL) { return -ERR_get_error (); } status = EVP_PKEY_keygen_init (ctx); if (status != 1) { status = -ERR_get_error (); goto exit; } status = EVP_PKEY_CTX_set_rsa_keygen_bits (ctx, bits); if (status != 1) { status = -ERR_get_error (); goto exit; } status = EVP_PKEY_keygen (ctx, &rsa); if (status != 1) { status = -ERR_get_error (); goto exit; } key->context = rsa; status = 0; exit: EVP_PKEY_CTX_free (ctx); return status; } int rsa_openssl_init_private_key (const struct rsa_engine *engine, struct rsa_private_key *key, const uint8_t *der, size_t length) { EVP_PKEY *rsa = NULL; int status; #if OPENSSL_IS_VERSION_3 const int ERROR_NOT_RSA = 0x1e08010c; #else const int ERROR_NOT_RSA = 0xd0680a8; #endif if ((engine == NULL) || (key == NULL) || (der == NULL) || (length == 0)) { return RSA_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); rsa = d2i_PrivateKey (EVP_PKEY_RSA, NULL, &der, length); if (rsa == NULL) { status = ERR_get_error (); if (status == ERROR_NOT_RSA) { status = RSA_ENGINE_NOT_RSA_KEY; } else { status = -status; } goto exit; } key->context = rsa; status = 0; exit: return status; } void rsa_openssl_release_key (const struct rsa_engine *engine, struct rsa_private_key *key) { if (engine && key) { EVP_PKEY_free ((EVP_PKEY*) key->context); memset (key, 0, sizeof (struct rsa_private_key)); } } int rsa_openssl_get_private_key_der (const struct rsa_engine *engine, const struct rsa_private_key *key, uint8_t **der, size_t *length) { int status; if (der == NULL) { return RSA_ENGINE_INVALID_ARGUMENT; } *der = NULL; if ((engine == NULL) || (key == NULL) || (length == NULL)) { return RSA_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); status = i2d_PrivateKey ((EVP_PKEY*) key->context, der); if (status >= 0) { *length = status; status = 0; } else { status = -ERR_get_error (); } return status; } int rsa_openssl_decrypt (const struct rsa_engine *engine, const struct rsa_private_key *key, const uint8_t *encrypted, size_t in_length, const uint8_t *label, size_t label_length, enum hash_type pad_hash, uint8_t *decrypted, size_t out_length) { EVP_PKEY_CTX *ctx = NULL; uint8_t *label_copy = NULL; int status; if ((engine == NULL) || (key == NULL) || (encrypted == NULL) || (in_length == 0) || (decrypted == NULL)) { return RSA_ENGINE_INVALID_ARGUMENT; } if (pad_hash > HASH_TYPE_SHA256) { return RSA_ENGINE_UNSUPPORTED_HASH_TYPE; } if ((int) out_length < EVP_PKEY_size ((EVP_PKEY*) key->context)) { return RSA_ENGINE_OUT_BUFFER_TOO_SMALL; } ERR_clear_error (); ctx = EVP_PKEY_CTX_new ((EVP_PKEY*) key->context, NULL); if (ctx == NULL) { return -ERR_get_error (); } status = EVP_PKEY_decrypt_init (ctx); if (status != 1) { status = -ERR_get_error (); goto exit; } status = EVP_PKEY_CTX_set_rsa_padding (ctx, RSA_PKCS1_OAEP_PADDING); if (status != 1) { status = -ERR_get_error (); goto exit; } if (pad_hash == HASH_TYPE_SHA256) { status = EVP_PKEY_CTX_set_rsa_oaep_md (ctx, EVP_sha256 ()); if (status != 1) { status = -ERR_get_error (); goto exit; } } /* A copy of the label needs to be made since context takes ownership of the label memory. */ if ((label != NULL) && (label_length != 0)) { label_copy = platform_malloc (label_length); if (label_copy == NULL) { status = RSA_ENGINE_NO_MEMORY; goto exit; } memcpy (label_copy, label, label_length); } /* Always set the label, since null pointers or 0 length will clear it. */ status = EVP_PKEY_CTX_set0_rsa_oaep_label (ctx, label_copy, label_length); if (status != 1) { status = -ERR_get_error (); goto exit; } status = EVP_PKEY_decrypt (ctx, decrypted, &out_length, encrypted, in_length); if (status == 1) { status = out_length; } else { status = -ERR_get_error (); } exit: EVP_PKEY_CTX_free (ctx); return status; } #endif #ifdef RSA_ENABLE_DER_PUBLIC_KEY int rsa_openssl_init_public_key (const struct rsa_engine *engine, struct rsa_public_key *key, const uint8_t *der, size_t length) { EVP_PKEY *rsa = NULL; int status = 0; if ((engine == NULL) || (key == NULL) || (der == NULL) || (length == 0)) { return RSA_ENGINE_INVALID_ARGUMENT; } memset (key, 0, sizeof (*key)); ERR_clear_error (); rsa = d2i_PUBKEY (NULL, &der, length); if (rsa == NULL) { return ERR_get_error (); } if (EVP_PKEY_base_id (rsa) != EVP_PKEY_RSA) { status = RSA_ENGINE_NOT_RSA_KEY; goto exit; } #if OPENSSL_IS_VERSION_3 { OSSL_PARAM *rsa_params = NULL; int i = 0; status = EVP_PKEY_todata (rsa, EVP_PKEY_PUBLIC_KEY, &rsa_params); if (status != 1) { status = -ERR_get_error (); goto exit; } while (rsa_params[i].key != NULL) { if (strcmp (rsa_params[i].key, "n") == 0) { BIGNUM *n = NULL; status = OSSL_PARAM_get_BN (&rsa_params[i], &n); if (status != 1) { status = -ERR_get_error (); goto exit_params; } key->mod_length = BN_num_bytes (n); if (key->mod_length > sizeof (key->modulus)) { status = RSA_ENGINE_UNSUPPORTED_KEY_LENGTH; BN_free (n); goto exit_params; } BN_bn2bin (n, key->modulus); BN_free (n); } else if (strcmp (rsa_params[i].key, "e") == 0) { BIGNUM *e = NULL; status = OSSL_PARAM_get_BN (&rsa_params[i], &e); if (status != 1) { status = -ERR_get_error (); goto exit_params; } if ((size_t) BN_num_bytes (e) > sizeof (key->exponent)) { status = RSA_ENGINE_UNSUPPORTED_KEY_LENGTH; BN_free (e); goto exit_params; } BN_bn2bin (e, (uint8_t*) &key->exponent); BN_free (e); } i++; } if ((key->mod_length == 0) || (key->exponent == 0)) { /* Missing some key data. */ status = RSA_ENGINE_PUBLIC_KEY_FAILED; } else { status = 0; } exit_params: OSSL_PARAM_free (rsa_params); } #else { RSA *rsa_key = EVP_PKEY_get0_RSA (rsa); uint8_t exp[4]; key->mod_length = BN_num_bytes (RSA_get0_n (rsa_key)); if (key->mod_length > sizeof (key->modulus)) { status = RSA_ENGINE_UNSUPPORTED_KEY_LENGTH; goto exit; } BN_bn2bin (RSA_get0_n (rsa_key), key->modulus); if (BN_num_bytes (RSA_get0_e (rsa_key)) > (int) sizeof (exp)) { status = RSA_ENGINE_UNSUPPORTED_KEY_LENGTH; goto exit; } memset (exp, 0, sizeof (exp)); BN_bn2bin (RSA_get0_e (rsa_key), exp); key->exponent = (exp[3] << 24) | (exp[2] << 16) | (exp[1] << 8) | exp[0]; } #endif exit: EVP_PKEY_free (rsa); return status; } int rsa_openssl_get_public_key_der (const struct rsa_engine *engine, const struct rsa_private_key *key, uint8_t **der, size_t *length) { int status; if (der == NULL) { return RSA_ENGINE_INVALID_ARGUMENT; } *der = NULL; if ((engine == NULL) || (key == NULL) || (length == NULL)) { return RSA_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); status = i2d_PUBKEY ((EVP_PKEY*) key->context, der); if (status >= 0) { *length = status; status = 0; } else { status = -ERR_get_error (); } return status; } #endif /** * Allocate an RSA context and load it with a public key. * * @param rsa The pointer for the new RSA context. * @param key The public key to load. * * @return 0 if the operation was successful or an error code. */ static int rsa_openssl_load_pubkey (EVP_PKEY **rsa, const struct rsa_public_key *key) { uint8_t exp[4]; BIGNUM *n = NULL; BIGNUM *e = NULL; int status; ERR_clear_error (); n = BN_bin2bn (key->modulus, key->mod_length, NULL); if (n == NULL) { return -ERR_get_error (); } exp[0] = key->exponent >> 24; exp[1] = key->exponent >> 16; exp[2] = key->exponent >> 8; exp[3] = key->exponent; e = BN_bin2bn (exp, sizeof (exp), NULL); if (e == NULL) { status = -ERR_get_error (); goto err_e; } #if OPENSSL_IS_VERSION_3 { OSSL_PARAM_BLD *param_list = NULL; OSSL_PARAM *rsa_params = NULL; EVP_PKEY_CTX *ctx = NULL; param_list = OSSL_PARAM_BLD_new (); if (param_list == NULL) { status = -ERR_get_error (); goto err_list; } status = OSSL_PARAM_BLD_push_BN (param_list, "n", n); if (status != 1) { status = -ERR_get_error (); goto err_push; } status = OSSL_PARAM_BLD_push_BN (param_list, "e", e); if (status != 1) { status = -ERR_get_error (); goto err_push; } rsa_params = OSSL_PARAM_BLD_to_param (param_list); if (rsa_params == NULL) { status = -ERR_get_error (); goto err_push; } ctx = EVP_PKEY_CTX_new_from_name (NULL, "RSA", NULL); if (ctx == NULL) { status = -ERR_get_error (); goto err_ctx; } status = EVP_PKEY_fromdata_init (ctx); if (status != 1) { status = -ERR_get_error (); goto err_key; } status = EVP_PKEY_fromdata (ctx, rsa, EVP_PKEY_PUBLIC_KEY, rsa_params); if (status != 1) { status = -ERR_get_error (); goto err_key; } status = 0; err_key: EVP_PKEY_CTX_free (ctx); err_ctx: OSSL_PARAM_free (rsa_params); err_push: OSSL_PARAM_BLD_free (param_list); } #else { RSA *rsa_key; rsa_key = RSA_new (); if (rsa_key == NULL) { status = -ERR_get_error (); goto err_list; } status = RSA_set0_key (rsa_key, n, e, NULL); if (status == 0) { status = -ERR_get_error (); goto err_rsa; } *rsa = EVP_PKEY_new (); if (*rsa == NULL) { status = -ERR_get_error (); goto err_rsa; } status = EVP_PKEY_assign_RSA (*rsa, rsa_key); if (status != 1) { status = -ERR_get_error (); goto err_rsa; } /* Don't free anything that was created. */ return 0; err_rsa: RSA_free (rsa_key); } #endif err_list: BN_free (e); err_e: BN_free (n); return status; } int rsa_openssl_sig_verify (const struct rsa_engine *engine, const struct rsa_public_key *key, const uint8_t *signature, size_t sig_length, enum hash_type sig_hash, const uint8_t *match, size_t match_length) { EVP_PKEY *rsa = NULL; EVP_PKEY_CTX *ctx = NULL; const EVP_MD *sig_algo; int status; if ((engine == NULL) || (key == NULL) || (signature == NULL) || (match == NULL) || (sig_length == 0) || (match_length == 0)) { return RSA_ENGINE_INVALID_ARGUMENT; } switch (sig_hash) { case HASH_TYPE_SHA256: sig_algo = EVP_sha256 (); break; case HASH_TYPE_SHA384: sig_algo = EVP_sha384 (); break; case HASH_TYPE_SHA512: sig_algo = EVP_sha512 (); break; default: return RSA_ENGINE_UNSUPPORTED_SIG_TYPE; } status = rsa_openssl_load_pubkey (&rsa, key); if (status != 0) { return status; } ctx = EVP_PKEY_CTX_new (rsa, NULL); if (ctx == NULL) { status = -ERR_get_error (); goto err_ctx; } status = EVP_PKEY_verify_init (ctx); if (status != 1) { status = -ERR_get_error (); goto err_params; } status = EVP_PKEY_CTX_set_rsa_padding (ctx, RSA_PKCS1_PADDING); if (status != 1) { status = -ERR_get_error (); goto err_params; } status = EVP_PKEY_CTX_set_signature_md (ctx, sig_algo); if (status != 1) { status = -ERR_get_error (); goto err_params; } if (EVP_PKEY_verify (ctx, signature, sig_length, match, match_length) == 1) { status = 0; } else { status = RSA_ENGINE_BAD_SIGNATURE; } err_params: EVP_PKEY_CTX_free (ctx); err_ctx: EVP_PKEY_free (rsa); return status; } /** * Initialize an openssl RSA engine. * * @param engine The RSA engine to initialize. * * @return 0 if the RSA engine was successfully initialize or an error code. */ int rsa_openssl_init (struct rsa_engine_openssl *engine) { if (engine == NULL) { return RSA_ENGINE_INVALID_ARGUMENT; } memset (engine, 0, sizeof (struct rsa_engine_openssl)); #ifdef RSA_ENABLE_PRIVATE_KEY engine->base.generate_key = rsa_openssl_generate_key; engine->base.init_private_key = rsa_openssl_init_private_key; engine->base.release_key = rsa_openssl_release_key; engine->base.get_private_key_der = rsa_openssl_get_private_key_der; engine->base.decrypt = rsa_openssl_decrypt; #endif #ifdef RSA_ENABLE_DER_PUBLIC_KEY engine->base.init_public_key = rsa_openssl_init_public_key; engine->base.get_public_key_der = rsa_openssl_get_public_key_der; #endif engine->base.sig_verify = rsa_openssl_sig_verify; return 0; } /** * Release the resources used by an openssl RSA engine. * * @param engine The RSA engine to release. */ void rsa_openssl_release (const struct rsa_engine_openssl *engine) { UNUSED (engine); }