Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,412 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* lws_genaes provides an AES abstraction api in lws that works the
* same whether you are using openssl or mbedtls hash functions underneath.
*/
#include "private-lib-core.h"
#if defined(LWS_WITH_JOSE)
#include "private-lib-jose.h"
#endif
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
int
lws_genaes_create(struct lws_genaes_ctx *ctx, enum enum_aes_operation op,
enum enum_aes_modes mode, struct lws_gencrypto_keyelem *el,
enum enum_aes_padding padding, void *engine)
{
int n = 0;
ctx->ctx = EVP_CIPHER_CTX_new();
if (!ctx->ctx)
return -1;
ctx->mode = mode;
ctx->k = el;
ctx->engine = engine;
ctx->init = 0;
ctx->op = op;
ctx->padding = padding;
switch (ctx->k->len) {
case 128 / 8:
switch (mode) {
case LWS_GAESM_KW:
#if defined(LWS_HAVE_EVP_aes_128_wrap)
EVP_CIPHER_CTX_set_flags(ctx->ctx,
EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
ctx->cipher = EVP_aes_128_wrap();
break;
#else
lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
__func__);
return -1;
#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_128_cbc();
break;
#if defined(LWS_HAVE_EVP_aes_128_cfb128)
case LWS_GAESM_CFB128:
ctx->cipher = EVP_aes_128_cfb128();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_cfb8)
case LWS_GAESM_CFB8:
ctx->cipher = EVP_aes_128_cfb8();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ctr)
case LWS_GAESM_CTR:
ctx->cipher = EVP_aes_128_ctr();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ecb)
case LWS_GAESM_ECB:
ctx->cipher = EVP_aes_128_ecb();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ofb)
case LWS_GAESM_OFB:
ctx->cipher = EVP_aes_128_ofb();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_xts)
case LWS_GAESM_XTS:
lwsl_err("%s: AES XTS requires double-length key\n",
__func__);
break;
#endif
case LWS_GAESM_GCM:
ctx->cipher = EVP_aes_128_gcm();
break;
default:
goto bail;
}
break;
case 192 / 8:
switch (mode) {
case LWS_GAESM_KW:
#if defined(LWS_HAVE_EVP_aes_128_wrap)
EVP_CIPHER_CTX_set_flags(ctx->ctx,
EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
ctx->cipher = EVP_aes_192_wrap();
break;
#else
lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
__func__);
return -1;
#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_192_cbc();
break;
#if defined(LWS_HAVE_EVP_aes_192_cfb128)
case LWS_GAESM_CFB128:
ctx->cipher = EVP_aes_192_cfb128();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_192_cfb8)
case LWS_GAESM_CFB8:
ctx->cipher = EVP_aes_192_cfb8();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ctr)
case LWS_GAESM_CTR:
ctx->cipher = EVP_aes_192_ctr();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ecb)
case LWS_GAESM_ECB:
ctx->cipher = EVP_aes_192_ecb();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ofb)
case LWS_GAESM_OFB:
ctx->cipher = EVP_aes_192_ofb();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_xts)
case LWS_GAESM_XTS:
lwsl_err("%s: AES XTS 192 invalid\n", __func__);
goto bail;
#endif
case LWS_GAESM_GCM:
ctx->cipher = EVP_aes_192_gcm();
break;
default:
goto bail;
}
break;
case 256 / 8:
switch (mode) {
case LWS_GAESM_KW:
#if defined(LWS_HAVE_EVP_aes_128_wrap)
EVP_CIPHER_CTX_set_flags(ctx->ctx,
EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
ctx->cipher = EVP_aes_256_wrap();
break;
#else
lwsl_err("%s: your OpenSSL lacks AES wrap apis, update it\n",
__func__);
return -1;
#endif
case LWS_GAESM_CBC:
ctx->cipher = EVP_aes_256_cbc();
break;
#if defined(LWS_HAVE_EVP_aes_256_cfb128)
case LWS_GAESM_CFB128:
ctx->cipher = EVP_aes_256_cfb128();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_256_cfb8)
case LWS_GAESM_CFB8:
ctx->cipher = EVP_aes_256_cfb8();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ctr)
case LWS_GAESM_CTR:
ctx->cipher = EVP_aes_256_ctr();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ecb)
case LWS_GAESM_ECB:
ctx->cipher = EVP_aes_256_ecb();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_ofb)
case LWS_GAESM_OFB:
ctx->cipher = EVP_aes_256_ofb();
break;
#endif
#if defined(LWS_HAVE_EVP_aes_128_xts)
case LWS_GAESM_XTS:
ctx->cipher = EVP_aes_128_xts();
break;
#endif
case LWS_GAESM_GCM:
ctx->cipher = EVP_aes_256_gcm();
break;
default:
goto bail;
}
break;
case 512 / 8:
switch (mode) {
#if defined(LWS_HAVE_EVP_aes_128_xts)
case LWS_GAESM_XTS:
ctx->cipher = EVP_aes_256_xts();
#endif
break;
default:
goto bail;
}
break;
default:
lwsl_err("%s: unsupported AES size %d bits\n", __func__,
ctx->k->len * 8);
goto bail;
}
switch (ctx->op) {
case LWS_GAESO_ENC:
n = EVP_EncryptInit_ex(ctx->ctx, ctx->cipher, ctx->engine,
NULL, NULL);
EVP_CIPHER_CTX_set_padding(ctx->ctx, (int)padding);
break;
case LWS_GAESO_DEC:
n = EVP_DecryptInit_ex(ctx->ctx, ctx->cipher, ctx->engine,
NULL, NULL);
EVP_CIPHER_CTX_set_padding(ctx->ctx, (int)padding);
break;
}
if (!n) {
lwsl_err("%s: cipher init failed (cipher %p)\n", __func__,
ctx->cipher);
lws_tls_err_describe_clear();
goto bail;
}
return 0;
bail:
EVP_CIPHER_CTX_free(ctx->ctx);
ctx->ctx = NULL;
return -1;
}
int
lws_genaes_destroy(struct lws_genaes_ctx *ctx, unsigned char *tag, size_t tlen)
{
uint8_t buf[256];
int outl = sizeof(buf), n = 0;
if (!ctx->ctx)
return 0;
if (ctx->init) {
switch (ctx->op) {
case LWS_GAESO_ENC:
if (EVP_EncryptFinal_ex(ctx->ctx, buf, &outl) != 1) {
lwsl_err("%s: enc final failed\n", __func__);
n = -1;
}
if (ctx->mode == LWS_GAESM_GCM) {
if (EVP_CIPHER_CTX_ctrl(ctx->ctx,
EVP_CTRL_GCM_GET_TAG,
ctx->taglen, tag) != 1) {
lwsl_err("get tag ctrl failed\n");
//lws_tls_err_describe_clear();
n = 1;
}
}
if (ctx->mode == LWS_GAESM_CBC)
memcpy(tag, buf, (unsigned int)outl);
break;
case LWS_GAESO_DEC:
if (EVP_DecryptFinal_ex(ctx->ctx, buf, &outl) != 1) {
lwsl_err("%s: dec final failed\n", __func__);
lws_tls_err_describe_clear();
n = -1;
}
break;
}
if (outl)
lwsl_debug("%s: final len %d\n", __func__, outl);
}
ctx->k = NULL;
EVP_CIPHER_CTX_free(ctx->ctx);
ctx->ctx = NULL;
return n;
}
int
lws_genaes_crypt(struct lws_genaes_ctx *ctx,
const uint8_t *in, size_t len, uint8_t *out,
uint8_t *iv_or_nonce_ctr_or_data_unit_16,
uint8_t *stream_block_16, size_t *nc_or_iv_off, int taglen)
{
int n = 0, outl, olen;
if (!ctx->init) {
EVP_CIPHER_CTX_set_key_length(ctx->ctx, (int)ctx->k->len);
if (ctx->mode == LWS_GAESM_GCM) {
n = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_IVLEN,
(int)*nc_or_iv_off, NULL);
if (n != 1) {
lwsl_err("%s: SET_IVLEN failed\n", __func__);
return -1;
}
memcpy(ctx->tag, stream_block_16, (unsigned int)taglen);
ctx->taglen = taglen;
}
switch (ctx->op) {
case LWS_GAESO_ENC:
n = EVP_EncryptInit_ex(ctx->ctx, NULL, NULL,
ctx->k->buf,
iv_or_nonce_ctr_or_data_unit_16);
break;
case LWS_GAESO_DEC:
if (ctx->mode == LWS_GAESM_GCM)
EVP_CIPHER_CTX_ctrl(ctx->ctx,
EVP_CTRL_GCM_SET_TAG,
ctx->taglen, ctx->tag);
n = EVP_DecryptInit_ex(ctx->ctx, NULL, NULL,
ctx->k->buf,
iv_or_nonce_ctr_or_data_unit_16);
break;
}
if (!n) {
lws_tls_err_describe_clear();
lwsl_err("%s: init failed (cipher %p)\n",
__func__, ctx->cipher);
return -1;
}
ctx->init = 1;
}
if (ctx->mode == LWS_GAESM_GCM && !out) {
/* AAD */
if (!len)
return 0;
switch (ctx->op) {
case LWS_GAESO_ENC:
n = EVP_EncryptUpdate(ctx->ctx, NULL, &olen, in, (int)len);
break;
case LWS_GAESO_DEC:
n = EVP_DecryptUpdate(ctx->ctx, NULL, &olen, in, (int)len);
break;
default:
return -1;
}
if (n != 1) {
lwsl_err("%s: set AAD failed\n", __func__);
lws_tls_err_describe_clear();
lwsl_hexdump_err(in, len);
return -1;
}
return 0;
}
switch (ctx->op) {
case LWS_GAESO_ENC:
n = EVP_EncryptUpdate(ctx->ctx, out, &outl, in, (int)len);
break;
case LWS_GAESO_DEC:
n = EVP_DecryptUpdate(ctx->ctx, out, &outl, in, (int)len);
break;
default:
return -1;
}
// lwsl_notice("discarding outl %d\n", (int)outl);
if (!n) {
lwsl_notice("%s: update failed\n", __func__);
lws_tls_err_describe_clear();
return -1;
}
return 0;
}

View File

@ -0,0 +1,89 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* lws-gencrypto openssl-specific common code
*/
#include "private-lib-core.h"
#include "private-lib-tls-openssl.h"
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
int
lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type)
{
int h = -1;
switch (hash_type) {
case LWS_GENHASH_TYPE_UNKNOWN:
break;
case LWS_GENHASH_TYPE_MD5:
h = NID_md5;
break;
case LWS_GENHASH_TYPE_SHA1:
h = NID_sha1;
break;
case LWS_GENHASH_TYPE_SHA256:
h = NID_sha256;
break;
case LWS_GENHASH_TYPE_SHA384:
h = NID_sha384;
break;
case LWS_GENHASH_TYPE_SHA512:
h = NID_sha512;
break;
}
return h;
}
const EVP_MD *
lws_gencrypto_openssl_hash_to_EVP_MD(enum lws_genhash_types hash_type)
{
const EVP_MD *h = NULL;
switch (hash_type) {
case LWS_GENHASH_TYPE_UNKNOWN:
break;
case LWS_GENHASH_TYPE_MD5:
h = EVP_md5();
break;
case LWS_GENHASH_TYPE_SHA1:
h = EVP_sha1();
break;
case LWS_GENHASH_TYPE_SHA256:
h = EVP_sha256();
break;
case LWS_GENHASH_TYPE_SHA384:
h = EVP_sha384();
break;
case LWS_GENHASH_TYPE_SHA512:
h = EVP_sha512();
break;
}
return h;
}

View File

@ -0,0 +1,715 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* lws_genec provides an EC abstraction api in lws that works the
* same whether you are using openssl or mbedtls crypto functions underneath.
*/
#include "private-lib-core.h"
#include "private-lib-tls-openssl.h"
#if !defined(OPENSSL_NO_EC) && defined(LWS_HAVE_EC_KEY_new_by_curve_name) && \
(OPENSSL_VERSION_NUMBER >= 0x30000000l) && \
!defined(LWS_SUPPRESS_DEPRECATED_API_WARNINGS)
/* msvc doesn't have #warning... */
#error "You probably need LWS_SUPPRESS_DEPRECATED_API_WARNINGS"
#endif
#if defined(USE_WOLFSSL)
#include "openssl/ecdh.h"
#endif
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
#if defined(USE_WOLFSSL)
EVP_PKEY * EVP_PKEY_CTX_get0_pkey(EVP_PKEY_CTX *p)
{
return p->pkey;
}
#endif
#if !defined(LWS_HAVE_ECDSA_SIG_set0)
static void
ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
{
if (pr != NULL)
*pr = sig->r;
if (ps != NULL)
*ps = sig->s;
}
static int
ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
{
if (r == NULL || s == NULL)
return 0;
BN_clear_free(sig->r);
BN_clear_free(sig->s);
sig->r = r;
sig->s = s;
return 1;
}
#endif
#if !defined(LWS_HAVE_BN_bn2binpad)
int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen)
{
int i;
#if !defined(USE_WOLFSSL)
BN_ULONG l;
#endif
#if !defined(LIBRESSL_VERSION_NUMBER) && !defined(USE_WOLFSSL)
bn_check_top(a);
#endif
i = BN_num_bytes(a);
/* Add leading zeroes if necessary */
if (tolen > i) {
memset(to, 0, (size_t)(tolen - i));
to += tolen - i;
}
#if defined(USE_WOLFSSL)
BN_bn2bin(a, to);
#else
while (i--) {
l = a->d[i / BN_BYTES];
*(to++) = (unsigned char)(l >> (8 * (i % BN_BYTES))) & 0xff;
}
#endif
return tolen;
}
#endif
const struct lws_ec_curves lws_ec_curves[4] = {
/*
* These are the curves we are willing to use by default...
*
* The 3 recommended+ (P-256) and optional curves in RFC7518 7.6
*
* Specific keys lengths from RFC8422 p20
*/
{ "P-256", NID_X9_62_prime256v1, 32 },
{ "P-384", NID_secp384r1, 48 },
{ "P-521", NID_secp521r1, 66 },
{ NULL, 0, 0 }
};
static int
lws_genec_eckey_import(int nid, EVP_PKEY *pkey,
const struct lws_gencrypto_keyelem *el)
{
EC_KEY *ec = EC_KEY_new_by_curve_name(nid);
BIGNUM *bn_d, *bn_x, *bn_y;
int n;
if (!ec)
return -1;
/*
* EC_KEY contains
*
* EC_GROUP * group
* EC_POINT * pub_key
* BIGNUM * priv_key (ie, d)
*/
bn_x = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_X].buf,
(int)el[LWS_GENCRYPTO_EC_KEYEL_X].len, NULL);
if (!bn_x) {
lwsl_err("%s: BN_bin2bn (x) fail\n", __func__);
goto bail;
}
bn_y = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_Y].buf,
(int)el[LWS_GENCRYPTO_EC_KEYEL_Y].len, NULL);
if (!bn_y) {
lwsl_err("%s: BN_bin2bn (y) fail\n", __func__);
goto bail1;
}
/*
* EC_KEY_set_public_key_affine_coordinates sets the public key for
* key based on its affine co-ordinates, i.e. it constructs an
* EC_POINT object based on the supplied x and y values and sets
* the public key to be this EC_POINT. It will also performs
* certain sanity checks on the key to confirm that it is valid.
*/
#if defined(USE_WOLFSSL)
n = wolfSSL_EC_POINT_set_affine_coordinates_GFp(ec->group,
ec->pub_key,
bn_x, bn_y,
NULL);
#else
n = EC_KEY_set_public_key_affine_coordinates(ec, bn_x, bn_y);
#endif
BN_free(bn_x);
BN_free(bn_y);
if (n != 1) {
lwsl_err("%s: EC_KEY_set_public_key_affine_coordinates fail:\n",
__func__);
lws_tls_err_describe_clear();
goto bail;
}
if (el[LWS_GENCRYPTO_EC_KEYEL_D].len) {
bn_d = BN_bin2bn(el[LWS_GENCRYPTO_EC_KEYEL_D].buf,
(int)el[LWS_GENCRYPTO_EC_KEYEL_D].len, NULL);
if (!bn_d) {
lwsl_err("%s: BN_bin2bn (d) fail\n", __func__);
goto bail;
}
n = EC_KEY_set_private_key(ec, bn_d);
BN_clear_free(bn_d);
if (n != 1) {
lwsl_err("%s: EC_KEY_set_private_key fail\n", __func__);
goto bail;
}
}
/* explicitly confirm the key pieces are consistent */
#if !defined(USE_WOLFSSL)
if (EC_KEY_check_key(ec) != 1) {
lwsl_err("%s: EC_KEY_set_private_key fail\n", __func__);
goto bail;
}
#endif
n = EVP_PKEY_assign_EC_KEY(pkey, ec);
if (n != 1) {
lwsl_err("%s: EVP_PKEY_set1_EC_KEY failed\n", __func__);
return -1;
}
return 0;
bail1:
BN_free(bn_x);
bail:
EC_KEY_free(ec);
return -1;
}
static int
lws_genec_keypair_import(struct lws_genec_ctx *ctx,
const struct lws_ec_curves *curve_table,
EVP_PKEY_CTX **pctx,
const struct lws_gencrypto_keyelem *el)
{
EVP_PKEY *pkey = NULL;
const struct lws_ec_curves *curve;
if (el[LWS_GENCRYPTO_EC_KEYEL_CRV].len < 4)
return -2;
curve = lws_genec_curve(curve_table,
(char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf);
if (!curve)
return -3;
if ((el[LWS_GENCRYPTO_EC_KEYEL_D].len &&
el[LWS_GENCRYPTO_EC_KEYEL_D].len != curve->key_bytes) ||
el[LWS_GENCRYPTO_EC_KEYEL_X].len != curve->key_bytes ||
el[LWS_GENCRYPTO_EC_KEYEL_Y].len != curve->key_bytes)
return -4;
ctx->has_private = !!el[LWS_GENCRYPTO_EC_KEYEL_D].len;
pkey = EVP_PKEY_new();
if (!pkey)
return -7;
if (lws_genec_eckey_import(curve->tls_lib_nid, pkey, el)) {
lwsl_err("%s: lws_genec_eckey_import fail\n", __func__);
goto bail;
}
*pctx = EVP_PKEY_CTX_new(pkey, NULL);
EVP_PKEY_free(pkey);
pkey = NULL;
if (!*pctx)
goto bail;
return 0;
bail:
if (pkey)
EVP_PKEY_free(pkey);
if (*pctx) {
EVP_PKEY_CTX_free(*pctx);
*pctx = NULL;
}
return -9;
}
int
lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table)
{
ctx->context = context;
ctx->ctx[0] = NULL;
ctx->ctx[1] = NULL;
ctx->curve_table = curve_table;
ctx->genec_alg = LEGENEC_ECDH;
return 0;
}
int
lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table)
{
ctx->context = context;
ctx->ctx[0] = NULL;
ctx->ctx[1] = NULL;
ctx->curve_table = curve_table;
ctx->genec_alg = LEGENEC_ECDSA;
return 0;
}
int
lws_genecdh_set_key(struct lws_genec_ctx *ctx, struct lws_gencrypto_keyelem *el,
enum enum_lws_dh_side side)
{
if (ctx->genec_alg != LEGENEC_ECDH)
return -1;
return lws_genec_keypair_import(ctx, ctx->curve_table, &ctx->ctx[side], el);
}
int
lws_genecdsa_set_key(struct lws_genec_ctx *ctx,
const struct lws_gencrypto_keyelem *el)
{
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
return lws_genec_keypair_import(ctx, ctx->curve_table, &ctx->ctx[0], el);
}
static void
lws_genec_keypair_destroy(EVP_PKEY_CTX **pctx)
{
if (!*pctx)
return;
// lwsl_err("%p\n", EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx)));
// EC_KEY_free(EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(*pctx)));
EVP_PKEY_CTX_free(*pctx);
*pctx = NULL;
}
void
lws_genec_destroy(struct lws_genec_ctx *ctx)
{
if (ctx->ctx[0])
lws_genec_keypair_destroy(&ctx->ctx[0]);
if (ctx->ctx[1])
lws_genec_keypair_destroy(&ctx->ctx[1]);
}
static int
lws_genec_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
const char *curve_name, struct lws_gencrypto_keyelem *el)
{
const struct lws_ec_curves *curve;
const EC_POINT *pubkey;
EVP_PKEY *pkey = NULL;
int ret = -29, n, m;
BIGNUM *bn[3];
EC_KEY *ec;
curve = lws_genec_curve(ctx->curve_table, curve_name);
if (!curve) {
lwsl_err("%s: curve '%s' not supported\n",
__func__, curve_name);
return -22;
}
ec = EC_KEY_new_by_curve_name(curve->tls_lib_nid);
if (!ec) {
lwsl_err("%s: unknown nid %d\n", __func__, curve->tls_lib_nid);
return -23;
}
if (EC_KEY_generate_key(ec) != 1) {
lwsl_err("%s: EC_KEY_generate_key failed\n", __func__);
goto bail;
}
pkey = EVP_PKEY_new();
if (!pkey)
goto bail;
if (EVP_PKEY_set1_EC_KEY(pkey, ec) != 1) {
lwsl_err("%s: EVP_PKEY_assign_EC_KEY failed\n", __func__);
goto bail1;
}
ctx->ctx[side] = EVP_PKEY_CTX_new(pkey, NULL);
if (!ctx->ctx[side]) {
lwsl_err("%s: EVP_PKEY_CTX_new failed\n", __func__);
goto bail1;
}
/*
* we need to capture the individual element BIGNUMs into
* lws_gencrypto_keyelem, so they can be serialized, used in jwk etc
*/
pubkey = EC_KEY_get0_public_key(ec);
if (!pubkey) {
lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__);
goto bail1;
}
bn[0] = BN_new();
bn[1] = (BIGNUM *)EC_KEY_get0_private_key(ec);
bn[2] = BN_new();
#if defined(LWS_HAVE_EC_POINT_get_affine_coordinates)
if (EC_POINT_get_affine_coordinates(EC_KEY_get0_group(ec),
#else
if (EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec),
#endif
pubkey, bn[0], bn[2], NULL) != 1) {
lwsl_err("%s: EC_POINT_get_affine_coordinates_GFp failed\n",
__func__);
goto bail2;
}
el[LWS_GENCRYPTO_EC_KEYEL_CRV].len = (uint32_t)strlen(curve_name) + 1;
el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf =
lws_malloc(el[LWS_GENCRYPTO_EC_KEYEL_CRV].len, "ec");
if (!el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf) {
lwsl_err("%s: OOM\n", __func__);
goto bail2;
}
strcpy((char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf, curve_name);
for (n = LWS_GENCRYPTO_EC_KEYEL_X; n < LWS_GENCRYPTO_EC_KEYEL_COUNT;
n++) {
el[n].len = curve->key_bytes;
el[n].buf = lws_malloc(curve->key_bytes, "ec");
if (!el[n].buf)
goto bail2;
m = BN_bn2binpad(bn[n - 1], el[n].buf, (int32_t)el[n].len);
if ((uint32_t)m != el[n].len)
goto bail2;
}
ctx->has_private = 1;
ret = 0;
bail2:
BN_clear_free(bn[0]);
BN_clear_free(bn[2]);
bail1:
EVP_PKEY_free(pkey);
bail:
EC_KEY_free(ec);
return ret;
}
int
lws_genecdh_new_keypair(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
const char *curve_name,
struct lws_gencrypto_keyelem *el)
{
if (ctx->genec_alg != LEGENEC_ECDH)
return -1;
return lws_genec_new_keypair(ctx, side, curve_name, el);
}
int
lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
struct lws_gencrypto_keyelem *el)
{
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
return lws_genec_new_keypair(ctx, LDHS_OURS, curve_name, el);
}
#if 0
int
lws_genecdsa_hash_sign(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type,
uint8_t *sig, size_t sig_len)
{
const EVP_MD *md = lws_gencrypto_openssl_hash_to_EVP_MD(hash_type);
EVP_MD_CTX *mdctx = NULL;
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
if (!md)
return -1;
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);
return (int)sig_len;
bail:
if (mdctx)
EVP_MD_CTX_free(mdctx);
return -1;
}
#endif
int
lws_genecdsa_hash_sign_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, int keybits,
uint8_t *sig, size_t sig_len)
{
int ret = -1, n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
size_t hs = lws_genhash_size(hash_type);
const BIGNUM *r = NULL, *s = NULL;
ECDSA_SIG *ecdsasig;
EC_KEY *eckey;
if (ctx->genec_alg != LEGENEC_ECDSA) {
lwsl_notice("%s: ctx alg %d\n", __func__, ctx->genec_alg);
return -1;
}
if (!ctx->has_private)
return -1;
if ((int)sig_len != (int)(keybytes * 2)) {
lwsl_notice("%s: sig buff %d < %d\n", __func__,
(int)sig_len, (int)(hs * 2));
return -1;
}
eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx[0]));
/*
* The ECDSA P-256 SHA-256 digital signature is generated as follows:
*
* 1. Generate a digital signature of the JWS Signing Input using ECDSA
* P-256 SHA-256 with the desired private key. The output will be
* the pair (R, S), where R and S are 256-bit unsigned integers.
*
* 2. Turn R and S into octet sequences in big-endian order, with each
* array being be 32 octets long. The octet sequence
* representations MUST NOT be shortened to omit any leading zero
* octets contained in the values.
*
* 3. Concatenate the two octet sequences in the order R and then S.
* (Note that many ECDSA implementations will directly produce this
* concatenation as their output.)
*
* 4. The resulting 64-octet sequence is the JWS Signature value.
*/
ecdsasig = ECDSA_do_sign(in, (int)hs, eckey);
EC_KEY_free(eckey);
if (!ecdsasig) {
lwsl_notice("%s: ECDSA_do_sign fail\n", __func__);
goto bail;
}
ECDSA_SIG_get0(ecdsasig, &r, &s);
/*
* in the 521-bit case, we have to pad the last byte as it only
* generates 65 bytes
*/
n = BN_bn2binpad(r, sig, keybytes);
if (n != keybytes) {
lwsl_notice("%s: bignum r fail %d %d\n", __func__, n, keybytes);
goto bail;
}
n = BN_bn2binpad(s, sig + keybytes, keybytes);
if (n != keybytes) {
lwsl_notice("%s: bignum s fail %d %d\n", __func__, n, keybytes);
goto bail;
}
ret = 0;
bail:
if (ecdsasig)
ECDSA_SIG_free(ecdsasig);
return ret;
}
/* in is the JWS Signing Input hash */
int
lws_genecdsa_hash_sig_verify_jws(struct lws_genec_ctx *ctx, const uint8_t *in,
enum lws_genhash_types hash_type, int keybits,
const uint8_t *sig, size_t sig_len)
{
int ret = -1, n, hlen = (int)lws_genhash_size(hash_type),
keybytes = lws_gencrypto_bits_to_bytes(keybits);
ECDSA_SIG *ecsig = ECDSA_SIG_new();
BIGNUM *r = NULL, *s = NULL;
EC_KEY *eckey;
if (!ecsig)
return -1;
if (ctx->genec_alg != LEGENEC_ECDSA)
goto bail;
if ((int)sig_len != keybytes * 2) {
lwsl_err("%s: sig buf size %d vs %d\n", __func__,
(int)sig_len, keybytes * 2);
goto bail;
}
/*
* 1. The JWS Signature value MUST be a 64-octet sequence. If it is
* not a 64-octet sequence, the validation has failed.
*
* 2. Split the 64-octet sequence into two 32-octet sequences. The
* first octet sequence represents R and the second S. The values R
* and S are represented as octet sequences using the Integer-to-
* OctetString Conversion defined in Section 2.3.7 of SEC1 [SEC1]
* (in big-endian octet order).
*
* 3. Submit the JWS Signing Input, R, S, and the public key (x, y) to
* the ECDSA P-256 SHA-256 validator.
*/
r = BN_bin2bn(sig, keybytes, NULL);
if (!r) {
lwsl_err("%s: BN_bin2bn (r) fail\n", __func__);
goto bail;
}
s = BN_bin2bn(sig + keybytes, keybytes, NULL);
if (!s) {
lwsl_err("%s: BN_bin2bn (s) fail\n", __func__);
goto bail1;
}
if (ECDSA_SIG_set0(ecsig, r, s) != 1) {
lwsl_err("%s: ECDSA_SIG_set0 fail\n", __func__);
goto bail1;
}
eckey = EVP_PKEY_get1_EC_KEY(EVP_PKEY_CTX_get0_pkey(ctx->ctx[0]));
n = ECDSA_do_verify(in, hlen, ecsig, eckey);
EC_KEY_free(eckey);
if (n != 1) {
lwsl_err("%s: ECDSA_do_verify fail, hlen %d\n", __func__, (int)hlen);
lws_tls_err_describe_clear();
goto bail;
}
ret = 0;
goto bail;
bail1:
if (r)
BN_free(r);
if (s)
BN_free(s);
bail:
ECDSA_SIG_free(ecsig);
return ret;
}
int
lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss,
int *ss_len)
{
int len, ret = -1;
EC_KEY *eckey[2];
if (!ctx->ctx[LDHS_OURS] || !ctx->ctx[LDHS_THEIRS]) {
lwsl_err("%s: both sides must be set up\n", __func__);
return -1;
}
eckey[LDHS_OURS] = EVP_PKEY_get1_EC_KEY(
EVP_PKEY_CTX_get0_pkey(ctx->ctx[LDHS_OURS]));
eckey[LDHS_THEIRS] = EVP_PKEY_get1_EC_KEY(
EVP_PKEY_CTX_get0_pkey(ctx->ctx[LDHS_THEIRS]));
len = (EC_GROUP_get_degree(EC_KEY_get0_group(eckey[LDHS_OURS])) + 7) / 8;
if (len <= *ss_len) {
#if defined(USE_WOLFSSL)
*ss_len = wolfSSL_ECDH_compute_key(
#else
*ss_len = ECDH_compute_key(
#endif
ss, (unsigned int)len,
EC_KEY_get0_public_key(eckey[LDHS_THEIRS]),
eckey[LDHS_OURS], NULL);
ret = -(*ss_len < 0);
}
EC_KEY_free(eckey[LDHS_OURS]);
EC_KEY_free(eckey[LDHS_THEIRS]);
return ret;
}

View File

@ -0,0 +1,257 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* lws_genhash provides a hash / hmac abstraction api in lws that works the
* same whether you are using openssl or mbedtls hash functions underneath.
*/
#include <private-lib-core.h>
#include <openssl/obj_mac.h>
#include <openssl/opensslv.h>
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
int
lws_genhash_init(struct lws_genhash_ctx *ctx, enum lws_genhash_types type)
{
ctx->type = (uint8_t)type;
ctx->mdctx = EVP_MD_CTX_create();
if (!ctx->mdctx)
return 1;
switch (ctx->type) {
case LWS_GENHASH_TYPE_MD5:
ctx->evp_type = EVP_md5();
break;
case LWS_GENHASH_TYPE_SHA1:
ctx->evp_type = EVP_sha1();
break;
case LWS_GENHASH_TYPE_SHA256:
ctx->evp_type = EVP_sha256();
break;
case LWS_GENHASH_TYPE_SHA384:
ctx->evp_type = EVP_sha384();
break;
case LWS_GENHASH_TYPE_SHA512:
ctx->evp_type = EVP_sha512();
break;
default:
return 1;
}
if (EVP_DigestInit_ex(ctx->mdctx, ctx->evp_type, NULL) != 1) {
EVP_MD_CTX_destroy(ctx->mdctx);
return 1;
}
return 0;
}
int
lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len)
{
if (!len)
return 0;
return EVP_DigestUpdate(ctx->mdctx, in, len) != 1;
}
int
lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result)
{
unsigned int len;
int ret = 0;
if (!ctx->mdctx)
return 0;
if (result)
ret = EVP_DigestFinal_ex(ctx->mdctx, result, &len) != 1;
(void)len;
EVP_MD_CTX_destroy(ctx->mdctx);
ctx->mdctx = NULL;
return ret;
}
#if defined(LWS_HAVE_EVP_PKEY_new_raw_private_key)
int
lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
const uint8_t *key, size_t key_len)
{
ctx->ctx = EVP_MD_CTX_create();
if (!ctx->ctx)
return -1;
ctx->evp_type = 0;
ctx->type = (uint8_t)type;
switch (type) {
case LWS_GENHMAC_TYPE_SHA256:
ctx->evp_type = EVP_sha256();
break;
case LWS_GENHMAC_TYPE_SHA384:
ctx->evp_type = EVP_sha384();
break;
case LWS_GENHMAC_TYPE_SHA512:
ctx->evp_type = EVP_sha512();
break;
default:
lwsl_err("%s: unknown HMAC type %d\n", __func__, type);
goto bail;
}
ctx->key = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, NULL, key, key_len);
if (!ctx->key)
goto bail;
if (EVP_DigestSignInit(ctx->ctx, NULL, ctx->evp_type, NULL, ctx->key) != 1)
goto bail1;
return 0;
bail1:
EVP_PKEY_free(ctx->key);
bail:
EVP_MD_CTX_free(ctx->ctx);
return -1;
}
int
lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len)
{
if (EVP_DigestSignUpdate(ctx->ctx, in, len) != 1)
return -1;
return 0;
}
int
lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result)
{
size_t size = (size_t)lws_genhmac_size(ctx->type);
int n;
n = EVP_DigestSignFinal(ctx->ctx, result, &size);
EVP_MD_CTX_free(ctx->ctx);
EVP_PKEY_free(ctx->key);
if (n != 1)
return -1;
return 0;
}
#else
int
lws_genhmac_init(struct lws_genhmac_ctx *ctx, enum lws_genhmac_types type,
const uint8_t *key, size_t key_len)
{
#if defined(LWS_HAVE_HMAC_CTX_new)
ctx->ctx = HMAC_CTX_new();
if (!ctx->ctx)
return -1;
#else
HMAC_CTX_init(&ctx->ctx);
#endif
ctx->evp_type = 0;
ctx->type = (uint8_t)type;
switch (type) {
case LWS_GENHMAC_TYPE_SHA256:
ctx->evp_type = EVP_sha256();
break;
case LWS_GENHMAC_TYPE_SHA384:
ctx->evp_type = EVP_sha384();
break;
case LWS_GENHMAC_TYPE_SHA512:
ctx->evp_type = EVP_sha512();
break;
default:
lwsl_err("%s: unknown HMAC type %d\n", __func__, type);
goto bail;
}
#if defined(LWS_HAVE_HMAC_CTX_new)
if (HMAC_Init_ex(ctx->ctx, key, (int)key_len, ctx->evp_type, NULL) != 1)
#else
if (HMAC_Init_ex(&ctx->ctx, key, (int)key_len, ctx->evp_type, NULL) != 1)
#endif
goto bail;
return 0;
bail:
#if defined(LWS_HAVE_HMAC_CTX_new)
HMAC_CTX_free(ctx->ctx);
#endif
return -1;
}
int
lws_genhmac_update(struct lws_genhmac_ctx *ctx, const void *in, size_t len)
{
#if defined(LWS_HAVE_HMAC_CTX_new)
#if defined(LIBRESSL_VERSION_NUMBER)
if (HMAC_Update(ctx->ctx, in, len) != 1)
#else
if (HMAC_Update(ctx->ctx, in, (int)len) != 1)
#endif
#else /* HMAC_CTX_new */
if (HMAC_Update(&ctx->ctx, in, len) != 1)
#endif
return -1;
return 0;
}
int
lws_genhmac_destroy(struct lws_genhmac_ctx *ctx, void *result)
{
unsigned int size = (unsigned int)lws_genhmac_size(ctx->type);
#if defined(LWS_HAVE_HMAC_CTX_new)
int n = HMAC_Final(ctx->ctx, result, &size);
HMAC_CTX_free(ctx->ctx);
#else
int n = HMAC_Final(&ctx->ctx, result, &size);
#endif
if (n != 1)
return -1;
return 0;
}
#endif

View File

@ -0,0 +1,413 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* lws_genrsa provides an RSA abstraction api in lws that works the
* same whether you are using openssl or mbedtls crypto functions underneath.
*/
#include "private-lib-core.h"
#include "private-lib-tls-openssl.h"
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
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;
}
int
lws_genrsa_create(struct lws_genrsa_ctx *ctx,
const 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, (int)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) && !defined(USE_WOLFSSL)
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;
}
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) && !defined(USE_WOLFSSL)
{
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(
(unsigned int)BN_num_bytes(mpi[n]), "genrsakey");
if (!el[n].buf)
goto cleanup;
el[n].len = (unsigned int)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
*/
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;
}
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;
}
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;
}
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;
}
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, (unsigned int)h, (uint8_t *)sig,
(unsigned 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;
}
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, (unsigned int)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,
#if defined(USE_WOLFSSL)
ctx->ctx->pkey)) {
#else
EVP_PKEY_CTX_get0_pkey(ctx->ctx))) {
#endif
lwsl_err("%s: EVP_DigestSignInit failed\n", __func__);
goto bail;
}
if (EVP_DigestSignUpdate(mdctx, in, (unsigned int)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 = (unsigned int)sig_len;
break;
default:
return -1;
}
return (int)used;
bail:
if (mdctx)
EVP_MD_CTX_free(mdctx);
return -1;
}
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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,491 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2022 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
typedef struct lws_tls_session_cache_openssl {
lws_dll2_t list;
SSL_SESSION *session;
lws_sorted_usec_list_t sul_ttl;
/* name is overallocated here */
} lws_tls_sco_t;
#define tlssess_loglevel LLL_INFO
#if (_LWS_ENABLED_LOGS & tlssess_loglevel)
#define lwsl_tlssess(...) _lws_log(tlssess_loglevel, __VA_ARGS__)
#else
#define lwsl_tlssess(...)
#endif
static void
__lws_tls_session_destroy(lws_tls_sco_t *ts)
{
lwsl_tlssess("%s: %s (%u)\n", __func__, (const char *)&ts[1],
ts->list.owner->count - 1);
lws_sul_cancel(&ts->sul_ttl);
SSL_SESSION_free(ts->session);
lws_dll2_remove(&ts->list); /* vh lock */
lws_free(ts);
}
static lws_tls_sco_t *
__lws_tls_session_lookup_by_name(struct lws_vhost *vh, const char *name)
{
lws_start_foreach_dll(struct lws_dll2 *, p,
lws_dll2_get_head(&vh->tls_sessions)) {
lws_tls_sco_t *ts = lws_container_of(p, lws_tls_sco_t, list);
const char *ts_name = (const char *)&ts[1];
if (!strcmp(name, ts_name))
return ts;
} lws_end_foreach_dll(p);
return NULL;
}
/*
* If possible, reuse an existing, cached session
*/
void
lws_tls_reuse_session(struct lws *wsi)
{
char tag[LWS_SESSION_TAG_LEN];
lws_tls_sco_t *ts;
if (!wsi->a.vhost ||
wsi->a.vhost->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
return;
lws_context_lock(wsi->a.context, __func__); /* -------------- cx { */
lws_vhost_lock(wsi->a.vhost); /* -------------- vh { */
if (lws_tls_session_tag_from_wsi(wsi, tag, sizeof(tag)))
goto bail;
ts = __lws_tls_session_lookup_by_name(wsi->a.vhost, tag);
if (!ts) {
lwsl_tlssess("%s: no existing session for %s\n", __func__, tag);
goto bail;
}
lwsl_tlssess("%s: %s\n", __func__, (const char *)&ts[1]);
if (!SSL_set_session(wsi->tls.ssl, ts->session)) {
lwsl_err("%s: session not set for %s\n", __func__, tag);
goto bail;
}
#if !defined(USE_WOLFSSL)
/* extend session lifetime */
SSL_SESSION_set_time(ts->session,
#if defined(OPENSSL_IS_BORINGSSL)
(unsigned long)
#else
(long)
#endif
time(NULL));
#endif
/* keep our session list sorted in lru -> mru order */
lws_dll2_remove(&ts->list);
lws_dll2_add_tail(&ts->list, &wsi->a.vhost->tls_sessions);
bail:
lws_vhost_unlock(wsi->a.vhost); /* } vh -------------- */
lws_context_unlock(wsi->a.context); /* } cx -------------- */
}
int
lws_tls_session_is_reused(struct lws *wsi)
{
#if defined(LWS_WITH_CLIENT)
struct lws *nwsi = lws_get_network_wsi(wsi);
if (!nwsi || !nwsi->tls.ssl)
return 0;
return (int)SSL_session_reused(nwsi->tls.ssl);
#else
return 0;
#endif
}
static int
lws_tls_session_destroy_dll(struct lws_dll2 *d, void *user)
{
lws_tls_sco_t *ts = lws_container_of(d, lws_tls_sco_t, list);
__lws_tls_session_destroy(ts);
return 0;
}
void
lws_tls_session_vh_destroy(struct lws_vhost *vh)
{
lws_dll2_foreach_safe(&vh->tls_sessions, NULL,
lws_tls_session_destroy_dll);
}
static void
lws_tls_session_expiry_cb(lws_sorted_usec_list_t *sul)
{
lws_tls_sco_t *ts = lws_container_of(sul, lws_tls_sco_t, sul_ttl);
struct lws_vhost *vh = lws_container_of(ts->list.owner,
struct lws_vhost, tls_sessions);
lws_context_lock(vh->context, __func__); /* -------------- cx { */
lws_vhost_lock(vh); /* -------------- vh { */
__lws_tls_session_destroy(ts);
lws_vhost_unlock(vh); /* } vh -------------- */
lws_context_unlock(vh->context); /* } cx -------------- */
}
static lws_tls_sco_t *
lws_tls_session_add_entry(struct lws_vhost *vh, const char *tag)
{
lws_tls_sco_t *ts;
size_t nl = strlen(tag);
if (vh->tls_sessions.count == (vh->tls_session_cache_max ?
vh->tls_session_cache_max : 10)) {
/*
* We have reached the vhost's session cache limit,
* prune the LRU / head
*/
ts = lws_container_of(vh->tls_sessions.head,
lws_tls_sco_t, list);
if (ts) { /* centos 7 ... */
lwsl_tlssess("%s: pruning oldest session\n", __func__);
lws_vhost_lock(vh); /* -------------- vh { */
__lws_tls_session_destroy(ts);
lws_vhost_unlock(vh); /* } vh -------------- */
}
}
ts = lws_malloc(sizeof(*ts) + nl + 1, __func__);
if (!ts)
return NULL;
memset(ts, 0, sizeof(*ts));
memcpy(&ts[1], tag, nl + 1);
lws_dll2_add_tail(&ts->list, &vh->tls_sessions);
return ts;
}
static int
lws_tls_session_new_cb(SSL *ssl, SSL_SESSION *sess)
{
struct lws *wsi = (struct lws *)SSL_get_ex_data(ssl,
openssl_websocket_private_data_index);
char tag[LWS_SESSION_TAG_LEN];
struct lws_vhost *vh;
lws_tls_sco_t *ts;
long ttl;
#if (_LWS_ENABLED_LOGS & tlssess_loglevel)
const char *disposition = "reuse";
#endif
if (!wsi) {
lwsl_warn("%s: can't get wsi from ssl privdata\n", __func__);
return 0;
}
vh = wsi->a.vhost;
if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
return 0;
if (lws_tls_session_tag_from_wsi(wsi, tag, sizeof(tag)))
return 0;
/* api return is long, although we only support setting
* default (300s) or max uint32_t */
ttl = SSL_SESSION_get_timeout(sess);
lws_context_lock(vh->context, __func__); /* -------------- cx { */
lws_vhost_lock(vh); /* -------------- vh { */
ts = __lws_tls_session_lookup_by_name(vh, tag);
if (!ts) {
ts = lws_tls_session_add_entry(vh, tag);
if (!ts)
goto bail;
lws_sul_schedule(wsi->a.context, wsi->tsi, &ts->sul_ttl,
lws_tls_session_expiry_cb,
ttl * LWS_US_PER_SEC);
#if (_LWS_ENABLED_LOGS & tlssess_loglevel)
disposition = "new";
#endif
/*
* We don't have to do a SSL_SESSION_up_ref() here, because
* we will return from this callback indicating that we kept the
* ref
*/
} else {
/*
* Give up our refcount on the session we are about to replace
* with a newer one
*/
SSL_SESSION_free(ts->session);
/* keep our session list sorted in lru -> mru order */
lws_dll2_remove(&ts->list);
lws_dll2_add_tail(&ts->list, &vh->tls_sessions);
}
ts->session = sess;
lws_vhost_unlock(vh); /* } vh -------------- */
lws_context_unlock(vh->context); /* } cx -------------- */
lwsl_tlssess("%s: %p: %s: %s %s, ttl %lds (%s:%u)\n", __func__,
sess, wsi->lc.gutag, disposition, tag, ttl, vh->name,
vh->tls_sessions.count);
/*
* indicate we will hold on to the SSL_SESSION reference, and take
* responsibility to call SSL_SESSION_free() on it ourselves
*/
return 1;
bail:
lws_vhost_unlock(vh); /* } vh -------------- */
lws_context_unlock(vh->context); /* } cx -------------- */
return 0;
}
#if defined(LWS_TLS_SYNTHESIZE_CB)
/*
* On openssl, there is an async cb coming when the server issues the session
* information on the link, so we can pick it up and update the cache at the
* right time.
*
* On mbedtls and some version at least of borning ssl, this cb is either not
* part of the tls library apis or fails to arrive.
*
* This synthetic cb is called instead for those build cases, scheduled for
* +500ms after the tls negotiation completed.
*/
void
lws_sess_cache_synth_cb(lws_sorted_usec_list_t *sul)
{
struct lws_lws_tls *tls = lws_container_of(sul, struct lws_lws_tls,
sul_cb_synth);
struct lws *wsi = lws_container_of(tls, struct lws, tls);
SSL_SESSION *sess;
if (lws_tls_session_is_reused(wsi))
return;
sess = SSL_get1_session(tls->ssl);
if (!sess)
return;
if (!SSL_SESSION_is_resumable(sess) || /* not worth caching, or... */
!lws_tls_session_new_cb(tls->ssl, sess)) { /* ...cb didn't keep it */
/*
* For now the policy if no session message after the wait,
* is just let it be. Typically the session info is sent
* early.
*/
SSL_SESSION_free(sess);
}
}
#endif
void
lws_tls_session_cache(struct lws_vhost *vh, uint32_t ttl)
{
long cmode;
if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
return;
cmode = SSL_CTX_get_session_cache_mode(vh->tls.ssl_client_ctx);
SSL_CTX_set_session_cache_mode(vh->tls.ssl_client_ctx,
(int)(cmode | SSL_SESS_CACHE_CLIENT));
SSL_CTX_sess_set_new_cb(vh->tls.ssl_client_ctx, lws_tls_session_new_cb);
if (!ttl)
return;
#if defined(OPENSSL_IS_BORINGSSL)
SSL_CTX_set_timeout(vh->tls.ssl_client_ctx, ttl);
#else
SSL_CTX_set_timeout(vh->tls.ssl_client_ctx, (long)ttl);
#endif
}
int
lws_tls_session_dump_save(struct lws_vhost *vh, const char *host, uint16_t port,
lws_tls_sess_cb_t cb_save, void *opq)
{
struct lws_tls_session_dump d;
lws_tls_sco_t *ts;
int ret = 1, bl;
void *v;
if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
return 1;
lws_tls_session_tag_discrete(vh->name, host, port, d.tag, sizeof(d.tag));
lws_context_lock(vh->context, __func__); /* -------------- cx { */
lws_vhost_lock(vh); /* -------------- vh { */
ts = __lws_tls_session_lookup_by_name(vh, d.tag);
if (!ts)
goto bail;
/* We have a ref on the session, exit via bail to clean it... */
bl = i2d_SSL_SESSION(ts->session, NULL);
if (!bl)
goto bail;
d.blob_len = (size_t)bl;
v = d.blob = lws_malloc(d.blob_len, __func__);
if (d.blob) {
/* this advances d.blob by the blob size ;-) */
i2d_SSL_SESSION(ts->session, (uint8_t **)&d.blob);
d.opaque = opq;
d.blob = v;
if (cb_save(vh->context, &d))
lwsl_notice("%s: save failed\n", __func__);
else
ret = 0;
lws_free(v);
}
bail:
lws_vhost_unlock(vh); /* } vh -------------- */
lws_context_unlock(vh->context); /* } cx -------------- */
return ret;
}
int
lws_tls_session_dump_load(struct lws_vhost *vh, const char *host, uint16_t port,
lws_tls_sess_cb_t cb_load, void *opq)
{
struct lws_tls_session_dump d;
lws_tls_sco_t *ts;
SSL_SESSION *sess = NULL; /* allow it to "bail" early */
void *v;
if (vh->options & LWS_SERVER_OPTION_DISABLE_TLS_SESSION_CACHE)
return 1;
d.opaque = opq;
lws_tls_session_tag_discrete(vh->name, host, port, d.tag, sizeof(d.tag));
lws_context_lock(vh->context, __func__); /* -------------- cx { */
lws_vhost_lock(vh); /* -------------- vh { */
ts = __lws_tls_session_lookup_by_name(vh, d.tag);
if (ts) {
/*
* Since we are getting this out of cold storage, we should
* not replace any existing session since it is likely newer
*/
lwsl_notice("%s: session already exists for %s\n", __func__,
d.tag);
goto bail1;
}
if (cb_load(vh->context, &d)) {
lwsl_warn("%s: load failed\n", __func__);
goto bail1;
}
/* the callback has allocated the blob and set d.blob / d.blob_len */
v = d.blob;
/* this advances d.blob by the blob size ;-) */
sess = d2i_SSL_SESSION(NULL, (const uint8_t **)&d.blob,
(long)d.blob_len);
free(v); /* user code will have used malloc() */
if (!sess) {
lwsl_warn("%s: d2i_SSL_SESSION failed\n", __func__);
goto bail;
}
lws_vhost_lock(vh); /* -------------- vh { */
ts = lws_tls_session_add_entry(vh, d.tag);
lws_vhost_unlock(vh); /* } vh -------------- */
if (!ts) {
lwsl_warn("%s: unable to add cache entry\n", __func__);
goto bail;
}
ts->session = sess;
lwsl_tlssess("%s: session loaded OK\n", __func__);
lws_vhost_unlock(vh); /* } vh -------------- */
lws_context_unlock(vh->context); /* } cx -------------- */
return 0;
bail:
SSL_SESSION_free(sess);
bail1:
lws_vhost_unlock(vh); /* } vh -------------- */
lws_context_unlock(vh->context); /* } cx -------------- */
return 1;
}

View File

@ -0,0 +1,603 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-tls-openssl.h"
int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
/*
* Care: many openssl apis return 1 for success. These are translated to the
* lws convention of 0 for success.
*/
int lws_openssl_describe_cipher(struct lws *wsi)
{
#if !defined(LWS_WITH_NO_LOGS) && !defined(USE_WOLFSSL)
int np = -1;
SSL *s = wsi->tls.ssl;
SSL_get_cipher_bits(s, &np);
lwsl_info("%s: %s: %s, %s, %d bits, %s\n", __func__, lws_wsi_tag(wsi),
SSL_get_cipher_name(s), SSL_get_cipher(s), np,
SSL_get_cipher_version(s));
#endif
return 0;
}
int lws_ssl_get_error(struct lws *wsi, int n)
{
int m;
unsigned long l;
char buf[160];
if (!wsi->tls.ssl)
return 99;
m = SSL_get_error(wsi->tls.ssl, n);
lwsl_debug("%s: %p %d -> %d (errno %d)\n", __func__, wsi->tls.ssl, n, m, LWS_ERRNO);
if (m == SSL_ERROR_SSL) {
if (!wsi->tls.err_helper[0]) {
/* Append first error for clarity */
l = ERR_get_error();
if (l) {
ERR_error_string_n(
#if defined(LWS_WITH_BORINGSSL)
(uint32_t)
#endif
l, buf, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
lws_strncpy(wsi->tls.err_helper, buf,
sizeof(wsi->tls.err_helper));
}
}
// Describe other errors
lws_tls_err_describe_clear();
}
// assert (LWS_ERRNO != 9);
return m;
}
#if defined(LWS_WITH_SERVER)
static int
lws_context_init_ssl_pem_passwd_cb(char *buf, int size, int rwflag,
void *userdata)
{
struct lws_context_creation_info * info =
(struct lws_context_creation_info *)userdata;
strncpy(buf, info->ssl_private_key_password, (unsigned int)size);
buf[size - 1] = '\0';
return (int)strlen(buf);
}
#endif
#if defined(LWS_WITH_CLIENT)
static int
lws_context_init_ssl_pem_passwd_client_cb(char *buf, int size, int rwflag,
void *userdata)
{
struct lws_context_creation_info * info =
(struct lws_context_creation_info *)userdata;
const char *p = info->ssl_private_key_password;
if (info->client_ssl_private_key_password)
p = info->client_ssl_private_key_password;
strncpy(buf, p, (unsigned int)size);
buf[size - 1] = '\0';
return (int)strlen(buf);
}
#endif
void
lws_ssl_bind_passphrase(SSL_CTX *ssl_ctx, int is_client,
const struct lws_context_creation_info *info)
{
if (
#if defined(LWS_WITH_SERVER)
!info->ssl_private_key_password
#endif
#if defined(LWS_WITH_SERVER) && defined(LWS_WITH_CLIENT)
&&
#endif
#if defined(LWS_WITH_CLIENT)
!info->client_ssl_private_key_password
#endif
)
return;
/*
* password provided, set ssl callback and user data
* for checking password which will be trigered during
* SSL_CTX_use_PrivateKey_file function
*/
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)info);
SSL_CTX_set_default_passwd_cb(ssl_ctx, is_client ?
#if defined(LWS_WITH_CLIENT)
lws_context_init_ssl_pem_passwd_client_cb:
#else
NULL:
#endif
#if defined(LWS_WITH_SERVER)
lws_context_init_ssl_pem_passwd_cb
#else
NULL
#endif
);
}
#if defined(LWS_WITH_CLIENT)
static void
lws_ssl_destroy_client_ctx(struct lws_vhost *vhost)
{
if (vhost->tls.user_supplied_ssl_ctx || !vhost->tls.ssl_client_ctx)
return;
if (vhost->tls.tcr && --vhost->tls.tcr->refcount)
return;
SSL_CTX_free(vhost->tls.ssl_client_ctx);
vhost->tls.ssl_client_ctx = NULL;
vhost->context->tls.count_client_contexts--;
if (vhost->tls.tcr) {
lws_dll2_remove(&vhost->tls.tcr->cc_list);
lws_free(vhost->tls.tcr);
vhost->tls.tcr = NULL;
}
}
#endif
void
lws_ssl_destroy(struct lws_vhost *vhost)
{
if (!lws_check_opt(vhost->context->options,
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return;
if (vhost->tls.ssl_ctx)
SSL_CTX_free(vhost->tls.ssl_ctx);
#if defined(LWS_WITH_CLIENT)
lws_ssl_destroy_client_ctx(vhost);
#endif
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
// <= 1.0.1f = old api, 1.0.1g+ = new api
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
ERR_remove_state(0);
#else
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
ERR_remove_thread_state();
#else
ERR_remove_thread_state(NULL);
#endif
#endif
/* not needed after 1.1.0 */
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && \
(OPENSSL_VERSION_NUMBER <= 0x10100000)
SSL_COMP_free_compression_methods();
#endif
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
#endif
}
int
lws_ssl_capable_read(struct lws *wsi, unsigned char *buf, size_t len)
{
struct lws_context *context = wsi->a.context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
int n = 0, m;
if (!wsi->tls.ssl)
return lws_ssl_capable_read_no_ssl(wsi, buf, len);
#ifndef WIN32
errno = 0;
#else
WSASetLastError(0);
#endif
ERR_clear_error();
n = SSL_read(wsi->tls.ssl, buf, (int)(ssize_t)len);
#if defined(LWS_PLAT_FREERTOS)
if (!n && errno == LWS_ENOTCONN) {
lwsl_debug("%s: SSL_read ENOTCONN\n", lws_wsi_tag(wsi));
return LWS_SSL_CAPABLE_ERROR;
}
#endif
lwsl_debug("%s: SSL_read says %d\n", lws_wsi_tag(wsi), n);
/* manpage: returning 0 means connection shut down
*
* 2018-09-10: https://github.com/openssl/openssl/issues/1903
*
* So, in summary, if you get a 0 or -1 return from SSL_read() /
* SSL_write(), you should call SSL_get_error():
*
* - If you get back SSL_ERROR_RETURN_ZERO then you know the connection
* has been cleanly shutdown by the peer. To fully close the
* connection you may choose to call SSL_shutdown() to send a
* close_notify back.
*
* - If you get back SSL_ERROR_SSL then some kind of internal or
* protocol error has occurred. More details will be on the SSL error
* queue. You can also call SSL_get_shutdown(). If this indicates a
* state of SSL_RECEIVED_SHUTDOWN then you know a fatal alert has
* been received from the peer (if it had been a close_notify then
* SSL_get_error() would have returned SSL_ERROR_RETURN_ZERO).
* SSL_ERROR_SSL is considered fatal - you should not call
* SSL_shutdown() in this case.
*
* - If you get back SSL_ERROR_SYSCALL then some kind of fatal (i.e.
* non-retryable) error has occurred in a system call.
*/
if (n <= 0) {
m = lws_ssl_get_error(wsi, n);
lwsl_debug("%s: ssl err %d errno %d\n", lws_wsi_tag(wsi), m, LWS_ERRNO);
if (m == SSL_ERROR_ZERO_RETURN) /* cleanly shut down */
goto do_err;
if (m == SSL_ERROR_SSL)
lws_tls_err_describe_clear();
/* hm not retryable.. could be 0 size pkt or error */
if (m == SSL_ERROR_SSL || m == SSL_ERROR_SYSCALL ||
LWS_ERRNO == LWS_ENOTCONN) {
/* unclean, eg closed conn */
wsi->socket_is_permanently_unusable = 1;
do_err:
#if defined(LWS_WITH_SYS_METRICS)
if (wsi->a.vhost)
lws_metric_event(wsi->a.vhost->mt_traffic_rx,
METRES_NOGO, 0);
#endif
return LWS_SSL_CAPABLE_ERROR;
}
/* retryable? */
if (SSL_want_read(wsi->tls.ssl)) {
lwsl_debug("%s: WANT_READ\n", __func__);
lwsl_debug("%s: LWS_SSL_CAPABLE_MORE_SERVICE\n", lws_wsi_tag(wsi));
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
if (SSL_want_write(wsi->tls.ssl)) {
lwsl_info("%s: WANT_WRITE\n", __func__);
lwsl_debug("%s: LWS_SSL_CAPABLE_MORE_SERVICE\n", lws_wsi_tag(wsi));
wsi->tls_read_wanted_write = 1;
lws_callback_on_writable(wsi);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
/* keep on trucking it seems */
}
#if defined(LWS_TLS_LOG_PLAINTEXT_RX)
/*
* If using openssl type tls library, this is the earliest point for all
* paths to dump what was received as decrypted data from the tls tunnel
*/
lwsl_notice("%s: len %d\n", __func__, n);
lwsl_hexdump_notice(buf, (unsigned int)n);
#endif
#if defined(LWS_WITH_SYS_METRICS)
if (wsi->a.vhost)
lws_metric_event(wsi->a.vhost->mt_traffic_rx, METRES_GO, (u_mt_t)n);
#endif
/*
* if it was our buffer that limited what we read,
* check if SSL has additional data pending inside SSL buffers.
*
* Because these won't signal at the network layer with POLLIN
* and if we don't realize, this data will sit there forever
*/
if (n != (int)(ssize_t)len)
goto bail;
if (!wsi->tls.ssl)
goto bail;
if (SSL_pending(wsi->tls.ssl)) {
if (lws_dll2_is_detached(&wsi->tls.dll_pending_tls))
lws_dll2_add_head(&wsi->tls.dll_pending_tls,
&pt->tls.dll_pending_tls_owner);
} else
__lws_ssl_remove_wsi_from_buffered_list(wsi);
return n;
bail:
lws_ssl_remove_wsi_from_buffered_list(wsi);
return n;
}
int
lws_ssl_pending(struct lws *wsi)
{
if (!wsi->tls.ssl)
return 0;
return SSL_pending(wsi->tls.ssl);
}
int
lws_ssl_capable_write(struct lws *wsi, unsigned char *buf, size_t len)
{
int n, m;
#if defined(LWS_TLS_LOG_PLAINTEXT_TX)
/*
* If using OpenSSL type tls library, this is the last point for all
* paths before sending data into the tls tunnel, where you can dump it
* and see what is being sent.
*/
lwsl_notice("%s: len %u\n", __func__, (unsigned int)len);
lwsl_hexdump_notice(buf, len);
#endif
if (!wsi->tls.ssl)
return lws_ssl_capable_write_no_ssl(wsi, buf, len);
errno = 0;
ERR_clear_error();
n = SSL_write(wsi->tls.ssl, buf, (int)(ssize_t)len);
if (n > 0) {
#if defined(LWS_WITH_SYS_METRICS)
if (wsi->a.vhost)
lws_metric_event(wsi->a.vhost->mt_traffic_tx,
METRES_GO, (u_mt_t)n);
#endif
return n;
}
m = lws_ssl_get_error(wsi, n);
if (m != SSL_ERROR_SYSCALL) {
if (m == SSL_ERROR_WANT_READ || SSL_want_read(wsi->tls.ssl)) {
lwsl_notice("%s: want read\n", __func__);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) {
lws_set_blocking_send(wsi);
lwsl_debug("%s: want write\n", __func__);
return LWS_SSL_CAPABLE_MORE_SERVICE;
}
}
lwsl_debug("%s failed: %s\n",__func__, ERR_error_string((unsigned int)m, NULL));
lws_tls_err_describe_clear();
wsi->socket_is_permanently_unusable = 1;
#if defined(LWS_WITH_SYS_METRICS)
if (wsi->a.vhost)
lws_metric_event(wsi->a.vhost->mt_traffic_tx,
METRES_NOGO, 0);
#endif
return LWS_SSL_CAPABLE_ERROR;
}
void
lws_ssl_info_callback(const SSL *ssl, int where, int ret)
{
struct lws *wsi;
struct lws_context *context;
struct lws_ssl_info si;
int fd;
#ifndef USE_WOLFSSL
context = (struct lws_context *)SSL_CTX_get_ex_data(
SSL_get_SSL_CTX(ssl),
openssl_SSL_CTX_private_data_index);
#else
context = (struct lws_context *)SSL_CTX_get_ex_data(
SSL_get_SSL_CTX((SSL*) ssl),
openssl_SSL_CTX_private_data_index);
#endif
if (!context)
return;
fd = SSL_get_fd(ssl);
if (fd < 0 || (fd - lws_plat_socket_offset()) < 0)
return;
wsi = wsi_from_fd(context, fd);
if (!wsi)
return;
if (!(where & wsi->a.vhost->tls.ssl_info_event_mask))
return;
si.where = where;
si.ret = ret;
if (user_callback_handle_rxflow(wsi->a.protocol->callback,
wsi, LWS_CALLBACK_SSL_INFO,
wsi->user_space, &si, 0))
lws_set_timeout(wsi, PENDING_TIMEOUT_KILLED_BY_SSL_INFO, -1);
}
int
lws_ssl_close(struct lws *wsi)
{
lws_sockfd_type n;
if (!wsi->tls.ssl)
return 0; /* not handled */
#if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK)
/* kill ssl callbacks, because we will remove the fd from the
* table linking it to the wsi
*/
if (wsi->a.vhost->tls.ssl_info_event_mask)
SSL_set_info_callback(wsi->tls.ssl, NULL);
#endif
#if defined(LWS_TLS_SYNTHESIZE_CB)
lws_sul_cancel(&wsi->tls.sul_cb_synth);
/*
* ... check the session in case it did not live long enough to get
* the scheduled callback to sample it
*/
lws_sess_cache_synth_cb(&wsi->tls.sul_cb_synth);
#endif
n = SSL_get_fd(wsi->tls.ssl);
if (!wsi->socket_is_permanently_unusable)
SSL_shutdown(wsi->tls.ssl);
compatible_close(n);
SSL_free(wsi->tls.ssl);
wsi->tls.ssl = NULL;
lws_tls_restrict_return(wsi);
// lwsl_notice("%s: ssl restr %d, simul %d\n", __func__,
// wsi->a.context->simultaneous_ssl_restriction,
// wsi->a.context->simultaneous_ssl);
return 1; /* handled */
}
void
lws_ssl_SSL_CTX_destroy(struct lws_vhost *vhost)
{
if (vhost->tls.ssl_ctx)
SSL_CTX_free(vhost->tls.ssl_ctx);
#if defined(LWS_WITH_CLIENT)
lws_ssl_destroy_client_ctx(vhost);
#endif
#if defined(LWS_WITH_ACME)
lws_tls_acme_sni_cert_destroy(vhost);
#endif
}
void
lws_ssl_context_destroy(struct lws_context *context)
{
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER < 0x10100000)
// <= 1.0.1f = old api, 1.0.1g+ = new api
#if (OPENSSL_VERSION_NUMBER <= 0x1000106f) || defined(USE_WOLFSSL)
ERR_remove_state(0);
#else
#if OPENSSL_VERSION_NUMBER >= 0x1010005f && \
!defined(LIBRESSL_VERSION_NUMBER) && \
!defined(OPENSSL_IS_BORINGSSL)
ERR_remove_thread_state();
#else
ERR_remove_thread_state(NULL);
#endif
#endif
// after 1.1.0 no need
#if (OPENSSL_VERSION_NUMBER >= 0x10002000) && (OPENSSL_VERSION_NUMBER <= 0x10100000)
SSL_COMP_free_compression_methods();
#endif
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
#endif
}
lws_tls_ctx *
lws_tls_ctx_from_wsi(struct lws *wsi)
{
if (!wsi->tls.ssl)
return NULL;
return SSL_get_SSL_CTX(wsi->tls.ssl);
}
enum lws_ssl_capable_status
__lws_tls_shutdown(struct lws *wsi)
{
int n;
#ifndef WIN32
errno = 0;
#else
WSASetLastError(0);
#endif
ERR_clear_error();
n = SSL_shutdown(wsi->tls.ssl);
lwsl_debug("SSL_shutdown=%d for fd %d\n", n, wsi->desc.sockfd);
switch (n) {
case 1: /* successful completion */
n = shutdown(wsi->desc.sockfd, SHUT_WR);
return LWS_SSL_CAPABLE_DONE;
case 0: /* needs a retry */
__lws_change_pollfd(wsi, 0, LWS_POLLIN);
return LWS_SSL_CAPABLE_MORE_SERVICE;
default: /* fatal error, or WANT */
n = SSL_get_error(wsi->tls.ssl, n);
if (n != SSL_ERROR_SYSCALL && n != SSL_ERROR_SSL) {
if (SSL_want_read(wsi->tls.ssl)) {
lwsl_debug("(wants read)\n");
__lws_change_pollfd(wsi, 0, LWS_POLLIN);
return LWS_SSL_CAPABLE_MORE_SERVICE_READ;
}
if (SSL_want_write(wsi->tls.ssl)) {
lwsl_debug("(wants write)\n");
__lws_change_pollfd(wsi, 0, LWS_POLLOUT);
return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE;
}
}
return LWS_SSL_CAPABLE_ERROR;
}
}
static int
tops_fake_POLLIN_for_buffered_openssl(struct lws_context_per_thread *pt)
{
return lws_tls_fake_POLLIN_for_buffered(pt);
}
const struct lws_tls_ops tls_ops_openssl = {
/* fake_POLLIN_for_buffered */ tops_fake_POLLIN_for_buffered_openssl,
};

View File

@ -0,0 +1,176 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-tls-openssl.h"
extern int openssl_websocket_private_data_index,
openssl_SSL_CTX_private_data_index;
#if defined(LWS_WITH_NETWORK)
static char openssl_ex_indexes_acquired;
#endif
void
lws_tls_err_describe_clear(void)
{
char buf[160];
unsigned long l;
do {
l = ERR_get_error();
if (!l)
break;
ERR_error_string_n(
#if defined(LWS_WITH_BORINGSSL)
(uint32_t)
#endif
l, buf, sizeof(buf));
lwsl_info(" openssl error: %s\n", buf);
} while (l);
lwsl_info("\n");
}
#if LWS_MAX_SMP != 1
static pthread_mutex_t *openssl_mutexes = NULL;
static void
lws_openssl_lock_callback(int mode, int type, const char *file, int line)
{
(void)file;
(void)line;
if (mode & CRYPTO_LOCK)
pthread_mutex_lock(&openssl_mutexes[type]);
else
pthread_mutex_unlock(&openssl_mutexes[type]);
}
static unsigned long
lws_openssl_thread_id(void)
{
#ifdef __PTW32_H
return (unsigned long)(intptr_t)(pthread_self()).p;
#else
return (unsigned long)pthread_self();
#endif
}
#endif
int
lws_context_init_ssl_library(struct lws_context *cx,
const struct lws_context_creation_info *info)
{
#ifdef USE_WOLFSSL
#ifdef USE_OLD_CYASSL
lwsl_cx_info(cx, " Compiled with CyaSSL support");
#else
lwsl_cx_info(cx, " Compiled with wolfSSL support");
#endif
#else
#if defined(LWS_WITH_BORINGSSL)
lwsl_cx_info(cx, " Compiled with BoringSSL support");
#else
lwsl_cx_info(cx, " Compiled with OpenSSL support");
#endif
#endif
if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) {
if (!info->provided_client_ssl_ctx)
lwsl_cx_info(cx, " SSL disabled: no "
"LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT");
return 0;
}
/* basic openssl init */
lwsl_cx_info(cx, "Doing SSL library init");
#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
#else
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
#endif
#if defined(LWS_WITH_NETWORK)
if (!openssl_ex_indexes_acquired) {
openssl_websocket_private_data_index =
SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL);
openssl_SSL_CTX_private_data_index =
SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
openssl_ex_indexes_acquired = 1;
}
#endif
#if LWS_MAX_SMP != 1
{
int n;
openssl_mutexes = (pthread_mutex_t *)
OPENSSL_malloc((size_t)((unsigned long)CRYPTO_num_locks() *
(unsigned long)sizeof(openssl_mutexes[0])));
for (n = 0; n < CRYPTO_num_locks(); n++)
pthread_mutex_init(&openssl_mutexes[n], NULL);
/*
* These "functions" disappeared in later OpenSSL which is
* already threadsafe.
*/
(void)lws_openssl_thread_id;
(void)lws_openssl_lock_callback;
CRYPTO_set_id_callback(lws_openssl_thread_id);
CRYPTO_set_locking_callback(lws_openssl_lock_callback);
}
#endif
return 0;
}
void
lws_context_deinit_ssl_library(struct lws_context *context)
{
#if LWS_MAX_SMP != 1
int n;
if (!lws_check_opt(context->options,
LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT))
return;
CRYPTO_set_locking_callback(NULL);
if (openssl_mutexes) {
for (n = 0; n < CRYPTO_num_locks(); n++)
pthread_mutex_destroy(&openssl_mutexes[n]);
OPENSSL_free(openssl_mutexes);
openssl_mutexes = NULL;
}
#endif
}

View File

@ -0,0 +1,854 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#define WIN32_LEAN_AND_MEAN
#include "private-lib-core.h"
#include "private-lib-tls-openssl.h"
#if !defined(LWS_PLAT_OPTEE)
static int
dec(char c)
{
return c - '0';
}
#endif
static time_t
lws_tls_openssl_asn1time_to_unix(ASN1_TIME *as)
{
#if !defined(LWS_PLAT_OPTEE)
const char *p = (const char *)as->data;
struct tm t;
/* [YY]YYMMDDHHMMSSZ */
memset(&t, 0, sizeof(t));
if (strlen(p) == 13) {
t.tm_year = (dec(p[0]) * 10) + dec(p[1]) + 100;
p += 2;
} else {
t.tm_year = (dec(p[0]) * 1000) + (dec(p[1]) * 100) +
(dec(p[2]) * 10) + dec(p[3]);
p += 4;
}
t.tm_mon = (dec(p[0]) * 10) + dec(p[1]) - 1;
p += 2;
t.tm_mday = (dec(p[0]) * 10) + dec(p[1]) - 1;
p += 2;
t.tm_hour = (dec(p[0]) * 10) + dec(p[1]);
p += 2;
t.tm_min = (dec(p[0]) * 10) + dec(p[1]);
p += 2;
t.tm_sec = (dec(p[0]) * 10) + dec(p[1]);
t.tm_isdst = 0;
return mktime(&t);
#else
return (time_t)-1;
#endif
}
#if defined(USE_WOLFSSL)
#define AUTHORITY_KEYID WOLFSSL_AUTHORITY_KEYID
#endif
int
lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type,
union lws_tls_cert_info_results *buf, size_t len)
{
#ifndef USE_WOLFSSL
const unsigned char *dp;
ASN1_OCTET_STRING *val;
AUTHORITY_KEYID *akid;
X509_EXTENSION *ext;
int tag, xclass, r = 1;
long xlen, loc;
#endif
X509_NAME *xn;
#if !defined(LWS_PLAT_OPTEE)
char *p, *p1;
size_t rl;
#endif
buf->ns.len = 0;
if (!x509)
return -1;
if (!len)
len = sizeof(buf->ns.name);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(X509_get_notBefore)
#define X509_get_notBefore(x) X509_getm_notBefore(x)
#define X509_get_notAfter(x) X509_getm_notAfter(x)
#endif
switch (type) {
case LWS_TLS_CERT_INFO_VALIDITY_FROM:
buf->time = lws_tls_openssl_asn1time_to_unix(
X509_get_notBefore(x509));
if (buf->time == (time_t)-1)
return -1;
break;
case LWS_TLS_CERT_INFO_VALIDITY_TO:
buf->time = lws_tls_openssl_asn1time_to_unix(
X509_get_notAfter(x509));
if (buf->time == (time_t)-1)
return -1;
break;
case LWS_TLS_CERT_INFO_COMMON_NAME:
#if defined(LWS_PLAT_OPTEE)
return -1;
#else
xn = X509_get_subject_name(x509);
if (!xn)
return -1;
X509_NAME_oneline(xn, buf->ns.name, (int)len - 2);
p = strstr(buf->ns.name, "/CN=");
if (p) {
p += 4;
p1 = strchr(p, '/');
if (p1)
rl = lws_ptr_diff_size_t(p1, p);
else
rl = strlen(p);
memmove(buf->ns.name, p, rl);
buf->ns.name[rl] = '\0';
}
buf->ns.len = (int)strlen(buf->ns.name);
return 0;
#endif
case LWS_TLS_CERT_INFO_ISSUER_NAME:
xn = X509_get_issuer_name(x509);
if (!xn)
return -1;
X509_NAME_oneline(xn, buf->ns.name, (int)len - 1);
buf->ns.len = (int)strlen(buf->ns.name);
return 0;
case LWS_TLS_CERT_INFO_USAGE:
#if defined(LWS_HAVE_X509_get_key_usage)
buf->usage = X509_get_key_usage(x509);
break;
#else
return -1;
#endif
case LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY:
{
#ifndef USE_WOLFSSL
size_t klen = (unsigned int)i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509), NULL);
uint8_t *tmp, *ptmp;
if (!klen || klen > len)
return -1;
tmp = (uint8_t *)OPENSSL_malloc(klen);
if (!tmp)
return -1;
ptmp = tmp;
if (i2d_X509_PUBKEY(
X509_get_X509_PUBKEY(x509), &ptmp) != (int)klen ||
!ptmp || lws_ptr_diff(ptmp, tmp) != (int)klen) {
lwsl_info("%s: cert public key extraction failed\n",
__func__);
if (ptmp)
OPENSSL_free(tmp);
return -1;
}
buf->ns.len = (int)klen;
memcpy(buf->ns.name, tmp, klen);
OPENSSL_free(tmp);
#endif
return 0;
}
case LWS_TLS_CERT_INFO_DER_RAW:
{
int der_len = i2d_X509(x509, NULL);
uint8_t *tmp = (uint8_t *)buf->ns.name;
buf->ns.len = der_len < 0 ? 0 : der_len;
if (der_len < 0 || (size_t)der_len > len)
return -1;
der_len = i2d_X509(x509, &tmp);
if (der_len < 0)
return -1;
return 0;
}
#ifndef USE_WOLFSSL
case LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID:
loc = X509_get_ext_by_NID(x509, NID_authority_key_identifier, -1);
if (loc < 0)
return 1;
ext = X509_get_ext(x509, (int)loc);
if (!ext)
return 1;
#ifndef USE_WOLFSSL
akid = (AUTHORITY_KEYID *)X509V3_EXT_d2i(ext);
#else
akid = (AUTHORITY_KEYID *)wolfSSL_X509V3_EXT_d2i(ext);
#endif
if (!akid || !akid->keyid)
return 1;
val = akid->keyid;
dp = (const unsigned char *)val->data;
xlen = val->length;
buf->ns.len = (int)xlen;
if (len < (size_t)buf->ns.len)
return -1;
memcpy(buf->ns.name, dp, (size_t)buf->ns.len);
AUTHORITY_KEYID_free(akid);
break;
case LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID_ISSUER:
loc = X509_get_ext_by_NID(x509, NID_authority_key_identifier, -1);
if (loc < 0)
return 1;
ext = X509_get_ext(x509, (int)loc);
if (!ext)
return 1;
#ifndef USE_WOLFSSL
akid = (AUTHORITY_KEYID *)X509V3_EXT_d2i(ext);
#else
akid = (AUTHORITY_KEYID *)wolfSSL_X509V3_EXT_d2i(ext);
#endif
if (!akid || !akid->issuer)
return 1;
#if defined(LWS_HAVE_OPENSSL_STACK)
{
const X509V3_EXT_METHOD* method = X509V3_EXT_get(ext);
STACK_OF(CONF_VALUE) *cv;
int j;
cv = i2v_GENERAL_NAMES((X509V3_EXT_METHOD*)method, akid->issuer, NULL);
if (!cv)
goto bail_ak;
for (j = 0; j < OPENSSL_sk_num((const OPENSSL_STACK *)&cv); j++) {
CONF_VALUE *nval = OPENSSL_sk_value((const OPENSSL_STACK *)&cv, j);
size_t ln = (nval->name ? strlen(nval->name) : 0),
lv = (nval->value ? strlen(nval->value) : 0),
l = ln + lv;
if (len > l) {
if (nval->name)
memcpy(buf->ns.name + buf->ns.len, nval->name, ln);
if (nval->value)
memcpy(buf->ns.name + buf->ns.len + ln, nval->value, lv);
buf->ns.len = (int)((size_t)buf->ns.len + l);
len -= l;
buf->ns.name[buf->ns.len] = '\0';
r = 0;
}
}
}
bail_ak:
#endif
AUTHORITY_KEYID_free(akid);
return r;
case LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID_SERIAL:
loc = X509_get_ext_by_NID(x509, NID_authority_key_identifier, -1);
if (loc < 0)
return 1;
ext = X509_get_ext(x509, (int)loc);
if (!ext)
return 1;
akid = (AUTHORITY_KEYID *)X509V3_EXT_d2i(ext);
if (!akid || !akid->serial)
return 1;
#if 0
// need to handle blobs, and ASN1_INTEGER_get_uint64 not
// available on older openssl
{
uint64_t res;
if (ASN1_INTEGER_get_uint64(&res, akid->serial) != 1)
break;
buf->ns.len = lws_snprintf(buf->ns.name, len, "%llu",
(unsigned long long)res);
}
#endif
break;
case LWS_TLS_CERT_INFO_SUBJECT_KEY_ID:
loc = X509_get_ext_by_NID(x509, NID_subject_key_identifier, -1);
if (loc < 0)
return 1;
ext = X509_get_ext(x509, (int)loc);
if (!ext)
return 1;
val = X509_EXTENSION_get_data(ext);
if (!val)
return 1;
#if defined(USE_WOLFSSL)
return 1;
#else
dp = (const unsigned char *)val->data;
if (ASN1_get_object(&dp, &xlen,
&tag, &xclass, val->length) & 0x80)
return -1;
if (tag != V_ASN1_OCTET_STRING) {
lwsl_notice("not octet string %d\n", (int)tag);
return 1;
}
#endif
buf->ns.len = (int)xlen;
if (len < (size_t)buf->ns.len)
return -1;
memcpy(buf->ns.name, dp, (size_t)buf->ns.len);
break;
#endif
default:
return -1;
}
return 0;
}
int
lws_x509_info(struct lws_x509_cert *x509, enum lws_tls_cert_info type,
union lws_tls_cert_info_results *buf, size_t len)
{
return lws_tls_openssl_cert_info(x509->cert, type, buf, len);
}
#if defined(LWS_WITH_NETWORK)
int
lws_tls_vhost_cert_info(struct lws_vhost *vhost, enum lws_tls_cert_info type,
union lws_tls_cert_info_results *buf, size_t len)
{
#if defined(LWS_HAVE_SSL_CTX_get0_certificate)
X509 *x509 = SSL_CTX_get0_certificate(vhost->tls.ssl_ctx);
return lws_tls_openssl_cert_info(x509, type, buf, len);
#else
lwsl_notice("openssl is too old to support %s\n", __func__);
return -1;
#endif
}
int
lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
union lws_tls_cert_info_results *buf, size_t len)
{
int rc = 0;
X509 *x509;
wsi = lws_get_network_wsi(wsi);
x509 = SSL_get_peer_certificate(wsi->tls.ssl);
if (!x509) {
lwsl_debug("no peer cert\n");
return -1;
}
switch (type) {
case LWS_TLS_CERT_INFO_VERIFIED:
buf->verified = SSL_get_verify_result(wsi->tls.ssl) ==
X509_V_OK;
break;
default:
rc = lws_tls_openssl_cert_info(x509, type, buf, len);
}
X509_free(x509);
return rc;
}
#endif
int
lws_x509_create(struct lws_x509_cert **x509)
{
*x509 = lws_malloc(sizeof(**x509), __func__);
if (*x509)
(*x509)->cert = NULL;
return !(*x509);
}
int
lws_x509_parse_from_pem(struct lws_x509_cert *x509, const void *pem, size_t len)
{
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, pem, (int)len);
x509->cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!x509->cert) {
lwsl_err("%s: unable to parse PEM cert\n", __func__);
lws_tls_err_describe_clear();
return -1;
}
return 0;
}
int
lws_x509_verify(struct lws_x509_cert *x509, struct lws_x509_cert *trusted,
const char *common_name)
{
char c[32], *p;
int ret;
if (common_name) {
X509_NAME *xn = X509_get_subject_name(x509->cert);
if (!xn)
return -1;
X509_NAME_oneline(xn, c, (int)sizeof(c) - 2);
p = strstr(c, "/CN=");
if (p)
p = p + 4;
else
p = c;
if (strcmp(p, common_name)) {
lwsl_err("%s: common name mismatch\n", __func__);
return -1;
}
}
ret = X509_check_issued(trusted->cert, x509->cert);
if (ret != X509_V_OK) {
lwsl_err("%s: unable to verify cert relationship\n", __func__);
lws_tls_err_describe_clear();
return -1;
}
return 0;
}
#if defined(LWS_WITH_JOSE)
int
lws_x509_public_to_jwk(struct lws_jwk *jwk, struct lws_x509_cert *x509,
const char *curves, int rsa_min_bits)
{
int id, n, ret = -1, count;
ASN1_OBJECT *obj = NULL;
const EC_POINT *ecpoint;
const EC_GROUP *ecgroup;
EC_KEY *ecpub = NULL;
X509_PUBKEY *pubkey;
RSA *rsapub = NULL;
BIGNUM *mpi[4];
EVP_PKEY *pkey;
memset(jwk, 0, sizeof(*jwk));
pubkey = X509_get_X509_PUBKEY(x509->cert);
if (!pubkey) {
lwsl_err("%s: missing pubkey alg in cert\n", __func__);
goto bail;
}
if (X509_PUBKEY_get0_param(&obj, NULL, NULL, NULL, pubkey) != 1) {
lwsl_err("%s: missing pubkey alg in cert\n", __func__);
goto bail;
}
id = OBJ_obj2nid(obj);
if (id == NID_undef) {
lwsl_err("%s: missing pubkey alg in cert\n", __func__);
goto bail;
}
lwsl_debug("%s: key type %d \"%s\"\n", __func__, id, OBJ_nid2ln(id));
pkey = X509_get_pubkey(x509->cert);
if (!pkey) {
lwsl_notice("%s: unable to extract pubkey", __func__);
goto bail;
}
switch (id) {
case NID_X9_62_id_ecPublicKey:
lwsl_debug("%s: EC key\n", __func__);
jwk->kty = LWS_GENCRYPTO_KTY_EC;
if (!curves) {
lwsl_err("%s: ec curves not allowed\n", __func__);
goto bail1;
}
ecpub = EVP_PKEY_get1_EC_KEY(pkey);
if (!ecpub) {
lwsl_notice("%s: missing EC pubkey\n", __func__);
goto bail1;
}
ecpoint = EC_KEY_get0_public_key(ecpub);
if (!ecpoint) {
lwsl_err("%s: EC_KEY_get0_public_key failed\n", __func__);
goto bail2;
}
ecgroup = EC_KEY_get0_group(ecpub);
if (!ecgroup) {
lwsl_err("%s: EC_KEY_get0_group failed\n", __func__);
goto bail2;
}
/* validate the curve against ones we allow */
if (lws_genec_confirm_curve_allowed_by_tls_id(curves,
EC_GROUP_get_curve_name(ecgroup), jwk))
/* already logged */
goto bail2;
mpi[LWS_GENCRYPTO_EC_KEYEL_CRV] = NULL;
mpi[LWS_GENCRYPTO_EC_KEYEL_X] = BN_new(); /* X */
mpi[LWS_GENCRYPTO_EC_KEYEL_D] = NULL;
mpi[LWS_GENCRYPTO_EC_KEYEL_Y] = BN_new(); /* Y */
#if defined(LWS_HAVE_EC_POINT_get_affine_coordinates)
if (EC_POINT_get_affine_coordinates(ecgroup, ecpoint,
#else
if (EC_POINT_get_affine_coordinates_GFp(ecgroup, ecpoint,
#endif
mpi[LWS_GENCRYPTO_EC_KEYEL_X],
mpi[LWS_GENCRYPTO_EC_KEYEL_Y],
NULL) != 1) {
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
lwsl_err("%s: EC_POINT_get_aff failed\n", __func__);
goto bail2;
}
count = LWS_GENCRYPTO_EC_KEYEL_COUNT;
n = LWS_GENCRYPTO_EC_KEYEL_X;
break;
case NID_rsaEncryption:
lwsl_debug("%s: rsa key\n", __func__);
jwk->kty = LWS_GENCRYPTO_KTY_RSA;
rsapub = EVP_PKEY_get1_RSA(pkey);
if (!rsapub) {
lwsl_notice("%s: missing RSA pubkey\n", __func__);
goto bail1;
}
if ((size_t)RSA_size(rsapub) * 8 < (size_t)rsa_min_bits) {
lwsl_err("%s: key bits %d less than minimum %d\n",
__func__, RSA_size(rsapub) * 8, rsa_min_bits);
goto bail2;
}
#if defined(LWS_HAVE_RSA_SET0_KEY)
/* we don't need d... but the api wants to write it */
RSA_get0_key(rsapub,
(const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_N],
(const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_E],
(const BIGNUM **)&mpi[LWS_GENCRYPTO_RSA_KEYEL_D]);
#else
mpi[LWS_GENCRYPTO_RSA_KEYEL_E] = rsapub->e;
mpi[LWS_GENCRYPTO_RSA_KEYEL_N] = rsapub->n;
mpi[LWS_GENCRYPTO_RSA_KEYEL_D] = NULL;
#endif
count = LWS_GENCRYPTO_RSA_KEYEL_D;
n = LWS_GENCRYPTO_RSA_KEYEL_E;
break;
default:
lwsl_err("%s: unknown NID\n", __func__);
goto bail2;
}
for (; n < count; n++) {
if (!mpi[n])
continue;
jwk->e[n].len = (unsigned int)BN_num_bytes(mpi[n]);
jwk->e[n].buf = lws_malloc(jwk->e[n].len, "certkeyimp");
if (!jwk->e[n].buf) {
if (id == NID_X9_62_id_ecPublicKey) {
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
}
goto bail2;
}
BN_bn2bin(mpi[n], jwk->e[n].buf);
}
if (id == NID_X9_62_id_ecPublicKey) {
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_X]);
BN_clear_free(mpi[LWS_GENCRYPTO_EC_KEYEL_Y]);
}
ret = 0;
bail2:
if (id == NID_X9_62_id_ecPublicKey)
EC_KEY_free(ecpub);
else
RSA_free(rsapub);
bail1:
EVP_PKEY_free(pkey);
bail:
/* jwk destroy will clean any partial state */
if (ret)
lws_jwk_destroy(jwk);
return ret;
}
static int
lws_x509_jwk_privkey_pem_pp_cb(char *buf, int size, int rwflag, void *u)
{
const char *pp = (const char *)u;
size_t n = strlen(pp);
if ((int)n > size - 1)
return -1;
memcpy(buf, pp, n + 1);
return (int)n;
}
int
lws_x509_jwk_privkey_pem(struct lws_context *cx, struct lws_jwk *jwk,
void *pem, size_t len, const char *passphrase)
{
BIO* bio = BIO_new(BIO_s_mem());
BIGNUM *mpi, *dummy[6];
EVP_PKEY *pkey = NULL;
EC_KEY *ecpriv = NULL;
RSA *rsapriv = NULL;
const BIGNUM *cmpi;
int n, m, ret = -1;
BIO_write(bio, pem, (int)len);
PEM_read_bio_PrivateKey(bio, &pkey, lws_x509_jwk_privkey_pem_pp_cb,
(void *)passphrase);
BIO_free(bio);
lws_explicit_bzero((void *)pem, len);
if (!pkey) {
lwsl_err("%s: unable to parse PEM privkey\n", __func__);
lws_tls_err_describe_clear();
return -1;
}
/* confirm the key type matches the existing jwk situation */
switch (jwk->kty) {
case LWS_GENCRYPTO_KTY_EC:
if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
lwsl_err("%s: jwk is EC but privkey isn't\n", __func__);
goto bail;
}
ecpriv = EVP_PKEY_get1_EC_KEY(pkey);
if (!ecpriv) {
lwsl_notice("%s: missing EC key\n", __func__);
goto bail;
}
cmpi = EC_KEY_get0_private_key(ecpriv);
/* quick size check first */
n = BN_num_bytes(cmpi);
if (jwk->e[LWS_GENCRYPTO_EC_KEYEL_Y].len != (uint32_t)n) {
lwsl_err("%s: jwk key size doesn't match\n", __func__);
goto bail1;
}
/* TODO.. check public curve / group + point */
jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len = (unsigned int)n;
jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf = lws_malloc((unsigned int)n, "ec");
if (!jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf)
goto bail1;
m = BN_bn2binpad(cmpi, jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].buf,
(int32_t)jwk->e[LWS_GENCRYPTO_EC_KEYEL_D].len);
if ((unsigned int)m != (unsigned int)BN_num_bytes(cmpi))
goto bail1;
break;
case LWS_GENCRYPTO_KTY_RSA:
if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_RSA) {
lwsl_err("%s: RSA jwk, non-RSA privkey\n", __func__);
goto bail;
}
rsapriv = EVP_PKEY_get1_RSA(pkey);
if (!rsapriv) {
lwsl_notice("%s: missing RSA key\n", __func__);
goto bail;
}
#if defined(LWS_HAVE_RSA_SET0_KEY) && !defined(USE_WOLFSSL)
RSA_get0_key(rsapriv, (const BIGNUM **)&dummy[0], /* n */
(const BIGNUM **)&dummy[1], /* e */
(const BIGNUM **)&mpi); /* d */
RSA_get0_factors(rsapriv, (const BIGNUM **)&dummy[4], /* p */
(const BIGNUM **)&dummy[5]); /* q */
#else
dummy[0] = rsapriv->n;
dummy[1] = rsapriv->e;
dummy[4] = rsapriv->p;
dummy[5] = rsapriv->q;
mpi = rsapriv->d;
#endif
/* quick size check first */
n = BN_num_bytes(mpi);
if (jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len != (uint32_t)n) {
lwsl_err("%s: jwk key size doesn't match\n", __func__);
goto bail1;
}
/* then check that n & e match what we got from the cert */
dummy[2] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].buf,
(int32_t)jwk->e[LWS_GENCRYPTO_RSA_KEYEL_N].len,
NULL);
dummy[3] = BN_bin2bn(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].buf,
(int32_t)jwk->e[LWS_GENCRYPTO_RSA_KEYEL_E].len,
NULL);
m = BN_cmp(dummy[2], dummy[0]) | BN_cmp(dummy[3], dummy[1]);
BN_clear_free(dummy[2]);
BN_clear_free(dummy[3]);
if (m) {
lwsl_err("%s: privkey doesn't match jwk pubkey\n",
__func__);
goto bail1;
}
/* accept d from the PEM privkey into the JWK */
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].len = (unsigned int)n;
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf = lws_malloc((unsigned int)n, "privjk");
if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf)
goto bail1;
BN_bn2bin(mpi, jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf);
/* accept p and q from the PEM privkey into the JWK */
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].len = (unsigned int)BN_num_bytes(dummy[4]);
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf = lws_malloc((unsigned int)n, "privjk");
if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf) {
lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf);
goto bail1;
}
BN_bn2bin(dummy[4], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf);
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].len = (unsigned int)BN_num_bytes(dummy[5]);
jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf = lws_malloc((unsigned int)n, "privjk");
if (!jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf) {
lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_D].buf);
lws_free_set_NULL(jwk->e[LWS_GENCRYPTO_RSA_KEYEL_P].buf);
goto bail1;
}
BN_bn2bin(dummy[5], jwk->e[LWS_GENCRYPTO_RSA_KEYEL_Q].buf);
break;
default:
lwsl_err("%s: JWK has unknown kty %d\n", __func__, jwk->kty);
return -1;
}
ret = 0;
bail1:
if (jwk->kty == LWS_GENCRYPTO_KTY_EC)
EC_KEY_free(ecpriv);
else
RSA_free(rsapriv);
bail:
EVP_PKEY_free(pkey);
return ret;
}
#endif
void
lws_x509_destroy(struct lws_x509_cert **x509)
{
if (!*x509)
return;
if ((*x509)->cert) {
X509_free((*x509)->cert);
(*x509)->cert = NULL;
}
lws_free_set_NULL(*x509);
}

View File

@ -0,0 +1,62 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* gencrypto openssl-specific helper declarations
*/
#if !defined(__LWS_PRIVATE_LIB_TLS_OPENSSL_H__)
#define __LWS_PRIVATE_LIB_TLS_OPENSSL_H__
/*
* one of these per different client context
* cc_owner is in lws_context.lws_context_tls
*/
struct lws_tls_client_reuse {
lws_tls_ctx *ssl_client_ctx;
uint8_t hash[32];
struct lws_dll2 cc_list;
int refcount;
int index;
};
typedef int (*next_proto_cb)(SSL *, const unsigned char **out,
unsigned char *outlen, const unsigned char *in,
unsigned int inlen, void *arg);
struct lws_x509_cert {
X509 *cert; /* X509 is opaque, this has to be a pointer */
};
int
lws_gencrypto_openssl_hash_to_NID(enum lws_genhash_types hash_type);
const EVP_MD *
lws_gencrypto_openssl_hash_to_EVP_MD(enum lws_genhash_types hash_type);
#if !defined(LWS_HAVE_BN_bn2binpad)
int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen);
#endif
#endif