mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-16 00:00:07 +01:00
404 lines
9 KiB
C
404 lines
9 KiB
C
/*
|
|
* libwebsockets - generic RSA api hiding the backend
|
|
*
|
|
* Copyright (C) 2017 - 2018 Andy Green <andy@warmcat.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation:
|
|
* version 2.1 of the License.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
* MA 02110-1301 USA
|
|
*
|
|
* lws_genrsa provides an RSA abstraction api in lws that works the
|
|
* same whether you are using openssl or mbedtls crypto functions underneath.
|
|
*/
|
|
#include "core/private.h"
|
|
#include "tls/openssl/private.h"
|
|
|
|
/*
|
|
* Care: many openssl apis return 1 for success. These are translated to the
|
|
* lws convention of 0 for success.
|
|
*/
|
|
|
|
LWS_VISIBLE void
|
|
lws_genrsa_destroy_elements(struct lws_gencrypto_keyelem *el)
|
|
{
|
|
lws_gencrypto_destroy_elements(el, LWS_GENCRYPTO_RSA_KEYEL_COUNT);
|
|
}
|
|
|
|
static int mode_map_crypt[] = { RSA_PKCS1_PADDING, RSA_PKCS1_OAEP_PADDING },
|
|
mode_map_sig[] = { RSA_PKCS1_PADDING, RSA_PKCS1_PSS_PADDING };
|
|
|
|
static int
|
|
rsa_pkey_wrap(struct lws_genrsa_ctx *ctx, RSA *rsa)
|
|
{
|
|
EVP_PKEY *pkey;
|
|
|
|
/* we have the RSA object filled up... wrap in a PKEY */
|
|
|
|
pkey = EVP_PKEY_new();
|
|
if (!pkey)
|
|
return 1;
|
|
|
|
/* bind the PKEY to the RSA key we just prepared */
|
|
|
|
if (EVP_PKEY_assign_RSA(pkey, rsa) != 1) {
|
|
lwsl_err("%s: EVP_PKEY_assign_RSA_KEY failed\n", __func__);
|
|
goto bail;
|
|
}
|
|
|
|
/* pepare our PKEY_CTX with the PKEY */
|
|
|
|
ctx->ctx = EVP_PKEY_CTX_new(pkey, NULL);
|
|
EVP_PKEY_free(pkey);
|
|
pkey = NULL;
|
|
if (!ctx->ctx)
|
|
goto bail;
|
|
|
|
return 0;
|
|
|
|
bail:
|
|
if (pkey)
|
|
EVP_PKEY_free(pkey);
|
|
|
|
return 1;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_genrsa_create(struct lws_genrsa_ctx *ctx, struct lws_gencrypto_keyelem *el,
|
|
struct lws_context *context, enum enum_genrsa_mode mode,
|
|
enum lws_genhash_types oaep_hashid)
|
|
{
|
|
int n;
|
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
ctx->context = context;
|
|
ctx->mode = mode;
|
|
|
|
/* Step 1:
|
|
*
|
|
* convert the MPI for e and n to OpenSSL BIGNUMs
|
|
*/
|
|
|
|
for (n = 0; n < 5; n++) {
|
|
ctx->bn[n] = BN_bin2bn(el[n].buf, el[n].len, NULL);
|
|
if (!ctx->bn[n]) {
|
|
lwsl_notice("mpi load failed\n");
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
/* Step 2:
|
|
*
|
|
* assemble the OpenSSL RSA from the BIGNUMs
|
|
*/
|
|
|
|
ctx->rsa = RSA_new();
|
|
if (!ctx->rsa) {
|
|
lwsl_notice("Failed to create RSA\n");
|
|
goto bail;
|
|
}
|
|
|
|
#if defined(LWS_HAVE_RSA_SET0_KEY)
|
|
if (RSA_set0_key(ctx->rsa, ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_N],
|
|
ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_E],
|
|
ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_D]) != 1) {
|
|
lwsl_notice("RSA_set0_key failed\n");
|
|
goto bail;
|
|
}
|
|
RSA_set0_factors(ctx->rsa, ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_P],
|
|
ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_Q]);
|
|
#else
|
|
ctx->rsa->e = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_E];
|
|
ctx->rsa->n = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_N];
|
|
ctx->rsa->d = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_D];
|
|
ctx->rsa->p = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_P];
|
|
ctx->rsa->q = ctx->bn[LWS_GENCRYPTO_RSA_KEYEL_Q];
|
|
#endif
|
|
|
|
if (!rsa_pkey_wrap(ctx, ctx->rsa))
|
|
return 0;
|
|
|
|
bail:
|
|
for (n = 0; n < 5; n++)
|
|
if (ctx->bn[n]) {
|
|
BN_clear_free(ctx->bn[n]);
|
|
ctx->bn[n] = NULL;
|
|
}
|
|
|
|
if (ctx->rsa) {
|
|
RSA_free(ctx->rsa);
|
|
ctx->rsa = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_genrsa_new_keypair(struct lws_context *context, struct lws_genrsa_ctx *ctx,
|
|
enum enum_genrsa_mode mode, struct lws_gencrypto_keyelem *el,
|
|
int bits)
|
|
{
|
|
BIGNUM *bn;
|
|
int n;
|
|
|
|
memset(ctx, 0, sizeof(*ctx));
|
|
ctx->context = context;
|
|
ctx->mode = mode;
|
|
|
|
ctx->rsa = RSA_new();
|
|
if (!ctx->rsa) {
|
|
lwsl_notice("Failed to create RSA\n");
|
|
return -1;
|
|
}
|
|
|
|
bn = BN_new();
|
|
if (!bn)
|
|
goto cleanup_1;
|
|
if (BN_set_word(bn, RSA_F4) != 1) {
|
|
BN_free(bn);
|
|
goto cleanup_1;
|
|
}
|
|
|
|
n = RSA_generate_key_ex(ctx->rsa, bits, bn, NULL);
|
|
BN_clear_free(bn);
|
|
if (n != 1)
|
|
goto cleanup_1;
|
|
|
|
#if defined(LWS_HAVE_RSA_SET0_KEY)
|
|
{
|
|
const BIGNUM *mpi[5];
|
|
|
|
RSA_get0_key(ctx->rsa, &mpi[LWS_GENCRYPTO_RSA_KEYEL_N],
|
|
&mpi[LWS_GENCRYPTO_RSA_KEYEL_E], &mpi[LWS_GENCRYPTO_RSA_KEYEL_D]);
|
|
RSA_get0_factors(ctx->rsa, &mpi[LWS_GENCRYPTO_RSA_KEYEL_P],
|
|
&mpi[LWS_GENCRYPTO_RSA_KEYEL_Q]);
|
|
#else
|
|
{
|
|
BIGNUM *mpi[5] = { ctx->rsa->e, ctx->rsa->n, ctx->rsa->d,
|
|
ctx->rsa->p, ctx->rsa->q, };
|
|
#endif
|
|
for (n = 0; n < 5; n++)
|
|
if (BN_num_bytes(mpi[n])) {
|
|
el[n].buf = lws_malloc(
|
|
BN_num_bytes(mpi[n]), "genrsakey");
|
|
if (!el[n].buf)
|
|
goto cleanup;
|
|
el[n].len = BN_num_bytes(mpi[n]);
|
|
BN_bn2bin(mpi[n], el[n].buf);
|
|
}
|
|
}
|
|
|
|
if (!rsa_pkey_wrap(ctx, ctx->rsa))
|
|
return 0;
|
|
|
|
cleanup:
|
|
for (n = 0; n < LWS_GENCRYPTO_RSA_KEYEL_COUNT; n++)
|
|
if (el[n].buf)
|
|
lws_free_set_NULL(el[n].buf);
|
|
cleanup_1:
|
|
RSA_free(ctx->rsa);
|
|
ctx->rsa = NULL;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* in_len must be less than RSA_size(rsa) - 11 for the PKCS #1 v1.5
|
|
* based padding modes
|
|
*/
|
|
|
|
LWS_VISIBLE int
|
|
lws_genrsa_public_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
|
|
size_t in_len, uint8_t *out)
|
|
{
|
|
int n = RSA_public_encrypt((int)in_len, in, out, ctx->rsa,
|
|
mode_map_crypt[ctx->mode]);
|
|
if (n < 0) {
|
|
lwsl_err("%s: RSA_public_encrypt failed\n", __func__);
|
|
lws_tls_err_describe_clear();
|
|
return -1;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_genrsa_private_encrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
|
|
size_t in_len, uint8_t *out)
|
|
{
|
|
int n = RSA_private_encrypt((int)in_len, in, out, ctx->rsa,
|
|
mode_map_crypt[ctx->mode]);
|
|
if (n < 0) {
|
|
lwsl_err("%s: RSA_private_encrypt failed\n", __func__);
|
|
lws_tls_err_describe_clear();
|
|
return -1;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_genrsa_public_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
|
|
size_t in_len, uint8_t *out, size_t out_max)
|
|
{
|
|
int n = RSA_public_decrypt((int)in_len, in, out, ctx->rsa,
|
|
mode_map_crypt[ctx->mode]);
|
|
if (n < 0) {
|
|
lwsl_err("%s: RSA_public_decrypt failed\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_genrsa_private_decrypt(struct lws_genrsa_ctx *ctx, const uint8_t *in,
|
|
size_t in_len, uint8_t *out, size_t out_max)
|
|
{
|
|
int n = RSA_private_decrypt((int)in_len, in, out, ctx->rsa,
|
|
mode_map_crypt[ctx->mode]);
|
|
if (n < 0) {
|
|
lwsl_err("%s: RSA_private_decrypt failed\n", __func__);
|
|
lws_tls_err_describe_clear();
|
|
return -1;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_genrsa_hash_sig_verify(struct lws_genrsa_ctx *ctx, const uint8_t *in,
|
|
enum lws_genhash_types hash_type, const uint8_t *sig,
|
|
size_t sig_len)
|
|
{
|
|
int n = lws_gencrypto_openssl_hash_to_NID(hash_type),
|
|
h = (int)lws_genhash_size(hash_type);
|
|
const EVP_MD *md = NULL;
|
|
|
|
if (n < 0)
|
|
return -1;
|
|
|
|
switch(ctx->mode) {
|
|
case LGRSAM_PKCS1_1_5:
|
|
n = RSA_verify(n, in, h, (uint8_t *)sig, (int)sig_len, ctx->rsa);
|
|
break;
|
|
case LGRSAM_PKCS1_OAEP_PSS:
|
|
md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type);
|
|
if (!md)
|
|
return -1;
|
|
|
|
#if defined(LWS_HAVE_RSA_verify_pss_mgf1)
|
|
n = RSA_verify_pss_mgf1(ctx->rsa, in, h, md, NULL, -1,
|
|
(uint8_t *)sig,
|
|
#else
|
|
n = RSA_verify_PKCS1_PSS(ctx->rsa, in, md, (uint8_t *)sig,
|
|
#endif
|
|
(int)sig_len);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
if (n != 1) {
|
|
lwsl_notice("%s: fail\n", __func__);
|
|
lws_tls_err_describe_clear();
|
|
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
LWS_VISIBLE int
|
|
lws_genrsa_hash_sign(struct lws_genrsa_ctx *ctx, const uint8_t *in,
|
|
enum lws_genhash_types hash_type, uint8_t *sig,
|
|
size_t sig_len)
|
|
{
|
|
int n = lws_gencrypto_openssl_hash_to_NID(hash_type),
|
|
h = (int)lws_genhash_size(hash_type);
|
|
unsigned int used = 0;
|
|
EVP_MD_CTX *mdctx = NULL;
|
|
const EVP_MD *md = NULL;
|
|
|
|
if (n < 0)
|
|
return -1;
|
|
|
|
switch(ctx->mode) {
|
|
case LGRSAM_PKCS1_1_5:
|
|
if (RSA_sign(n, in, h, sig, &used, ctx->rsa) != 1) {
|
|
lwsl_err("%s: RSA_sign failed\n", __func__);
|
|
|
|
goto bail;
|
|
}
|
|
break;
|
|
|
|
case LGRSAM_PKCS1_OAEP_PSS:
|
|
|
|
md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type);
|
|
if (!md)
|
|
return -1;
|
|
|
|
if (EVP_PKEY_CTX_set_rsa_padding(ctx->ctx,
|
|
mode_map_sig[ctx->mode]) != 1) {
|
|
lwsl_err("%s: set_rsa_padding failed\n", __func__);
|
|
|
|
goto bail;
|
|
}
|
|
|
|
mdctx = EVP_MD_CTX_create();
|
|
if (!mdctx)
|
|
goto bail;
|
|
|
|
if (EVP_DigestSignInit(mdctx, NULL, md, NULL,
|
|
EVP_PKEY_CTX_get0_pkey(ctx->ctx))) {
|
|
lwsl_err("%s: EVP_DigestSignInit failed\n", __func__);
|
|
|
|
goto bail;
|
|
}
|
|
if (EVP_DigestSignUpdate(mdctx, in, EVP_MD_size(md))) {
|
|
lwsl_err("%s: EVP_DigestSignUpdate failed\n", __func__);
|
|
|
|
goto bail;
|
|
}
|
|
if (EVP_DigestSignFinal(mdctx, sig, &sig_len)) {
|
|
lwsl_err("%s: EVP_DigestSignFinal failed\n", __func__);
|
|
|
|
goto bail;
|
|
}
|
|
EVP_MD_CTX_free(mdctx);
|
|
used = (int)sig_len;
|
|
break;
|
|
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return used;
|
|
|
|
bail:
|
|
if (mdctx)
|
|
EVP_MD_CTX_free(mdctx);
|
|
|
|
return -1;
|
|
}
|
|
|
|
LWS_VISIBLE void
|
|
lws_genrsa_destroy(struct lws_genrsa_ctx *ctx)
|
|
{
|
|
if (!ctx->ctx)
|
|
return;
|
|
|
|
EVP_PKEY_CTX_free(ctx->ctx);
|
|
ctx->ctx = NULL;
|
|
ctx->rsa = NULL;
|
|
}
|