1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-16 00:00:07 +01:00
libwebsockets/lib/tls/mbedtls/lws-genec.c
Andy Green c9731c5f17 type comparisons: fixes
This is a huge patch that should be a global NOP.

For unix type platforms it enables -Wconversion to issue warnings (-> error)
for all automatic casts that seem less than ideal but are normally concealed
by the toolchain.

This is things like passing an int to a size_t argument.  Once enabled, I
went through all args on my default build (which build most things) and
tried to make the removed default cast explicit.

With that approach it neither change nor bloat the code, since it compiles
to whatever it was doing before, just with the casts made explicit... in a
few cases I changed some length args from int to size_t but largely left
the causes alone.

From now on, new code that is relying on less than ideal casting
will complain and nudge me to improve it by warnings.
2021-01-05 10:56:38 +00:00

522 lines
13 KiB
C

/*
* 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-mbedtls.h"
const struct lws_ec_curves lws_ec_curves[] = {
/*
* 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", MBEDTLS_ECP_DP_SECP256R1, 32 },
{ "P-384", MBEDTLS_ECP_DP_SECP384R1, 48 },
{ "P-521", MBEDTLS_ECP_DP_SECP521R1, 66 },
{ NULL, 0, 0 }
};
static int
lws_genec_keypair_import(struct lws_genec_ctx *ctx, enum enum_lws_dh_side side,
struct lws_gencrypto_keyelem *el)
{
const struct lws_ec_curves *curve;
mbedtls_ecp_keypair kp;
int ret = -1;
if (el[LWS_GENCRYPTO_EC_KEYEL_CRV].len < 4) {
lwsl_notice("%s: crv '%s' (%d)\n", __func__,
el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf ?
(char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf :
"null",
el[LWS_GENCRYPTO_EC_KEYEL_CRV].len);
return -21;
}
curve = lws_genec_curve(ctx->curve_table,
(char *)el[LWS_GENCRYPTO_EC_KEYEL_CRV].buf);
if (!curve)
return -22;
/*
* d (the private part) may be missing, otherwise it and everything
* else must match the expected bignum size
*/
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 -23;
mbedtls_ecp_keypair_init(&kp);
if (mbedtls_ecp_group_load(&kp.grp, (mbedtls_ecp_group_id)curve->tls_lib_nid))
goto bail1;
ctx->has_private = !!el[LWS_GENCRYPTO_EC_KEYEL_D].len;
/* d (the private key) is directly an mpi */
if (ctx->has_private &&
mbedtls_mpi_read_binary(&kp.d, el[LWS_GENCRYPTO_EC_KEYEL_D].buf,
el[LWS_GENCRYPTO_EC_KEYEL_D].len))
goto bail1;
mbedtls_ecp_set_zero(&kp.Q);
if (mbedtls_mpi_read_binary(&kp.Q.X, el[LWS_GENCRYPTO_EC_KEYEL_X].buf,
el[LWS_GENCRYPTO_EC_KEYEL_X].len))
goto bail1;
if (mbedtls_mpi_read_binary(&kp.Q.Y, el[LWS_GENCRYPTO_EC_KEYEL_Y].buf,
el[LWS_GENCRYPTO_EC_KEYEL_Y].len))
goto bail1;
mbedtls_mpi_lset(&kp.Q.Z, 1);
switch (ctx->genec_alg) {
case LEGENEC_ECDH:
if (mbedtls_ecdh_get_params(ctx->u.ctx_ecdh, &kp,
(mbedtls_ecdh_side)side))
goto bail1;
/* verify the key is consistent with the claimed curve */
if (ctx->has_private &&
mbedtls_ecp_check_privkey(&ctx->u.ctx_ecdh->grp,
&ctx->u.ctx_ecdh->d))
goto bail1;
if (mbedtls_ecp_check_pubkey(&ctx->u.ctx_ecdh->grp,
&ctx->u.ctx_ecdh->Q))
goto bail1;
break;
case LEGENEC_ECDSA:
if (mbedtls_ecdsa_from_keypair(ctx->u.ctx_ecdsa, &kp))
goto bail1;
/* verify the key is consistent with the claimed curve */
if (ctx->has_private &&
mbedtls_ecp_check_privkey(&ctx->u.ctx_ecdsa->grp,
&ctx->u.ctx_ecdsa->d))
goto bail1;
if (mbedtls_ecp_check_pubkey(&ctx->u.ctx_ecdsa->grp,
&ctx->u.ctx_ecdsa->Q))
goto bail1;
break;
default:
goto bail1;
}
ret = 0;
bail1:
mbedtls_ecp_keypair_free(&kp);
return ret;
}
int
lws_genecdh_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table)
{
memset(ctx, 0, sizeof(*ctx));
ctx->context = context;
ctx->curve_table = curve_table;
ctx->genec_alg = LEGENEC_ECDH;
ctx->u.ctx_ecdh = lws_zalloc(sizeof(*ctx->u.ctx_ecdh), "genecdh");
if (!ctx->u.ctx_ecdh)
return 1;
mbedtls_ecdh_init(ctx->u.ctx_ecdh);
return 0;
}
int
lws_genecdsa_create(struct lws_genec_ctx *ctx, struct lws_context *context,
const struct lws_ec_curves *curve_table)
{
memset(ctx, 0, sizeof(*ctx));
ctx->context = context;
ctx->curve_table = curve_table;
ctx->genec_alg = LEGENEC_ECDSA;
ctx->u.ctx_ecdsa = lws_zalloc(sizeof(*ctx->u.ctx_ecdsa), "genecdsa");
if (!ctx->u.ctx_ecdsa)
return 1;
mbedtls_ecdsa_init(ctx->u.ctx_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, side, el);
}
int
lws_genecdsa_set_key(struct lws_genec_ctx *ctx,
struct lws_gencrypto_keyelem *el)
{
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
return lws_genec_keypair_import(ctx, 0, el);
}
void
lws_genec_destroy(struct lws_genec_ctx *ctx)
{
switch (ctx->genec_alg) {
case LEGENEC_ECDH:
if (ctx->u.ctx_ecdh) {
mbedtls_ecdh_free(ctx->u.ctx_ecdh);
lws_free(ctx->u.ctx_ecdh);
ctx->u.ctx_ecdh = NULL;
}
break;
case LEGENEC_ECDSA:
if (ctx->u.ctx_ecdsa) {
mbedtls_ecdsa_free(ctx->u.ctx_ecdsa);
lws_free(ctx->u.ctx_ecdsa);
ctx->u.ctx_ecdsa = NULL;
}
break;
default:
break;
}
}
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)
{
const struct lws_ec_curves *curve;
mbedtls_ecdsa_context ecdsa;
mbedtls_ecp_keypair *kp;
mbedtls_mpi *mpi[3];
int n;
if (ctx->genec_alg != LEGENEC_ECDH)
return -1;
curve = lws_genec_curve(ctx->curve_table, curve_name);
if (!curve) {
lwsl_err("%s: curve '%s' not supported\n",
__func__, curve_name);
return -22;
}
mbedtls_ecdsa_init(&ecdsa);
n = mbedtls_ecdsa_genkey(&ecdsa, (mbedtls_ecp_group_id)curve->tls_lib_nid,
lws_gencrypto_mbedtls_rngf,
ctx->context);
if (n) {
lwsl_err("mbedtls_ecdsa_genkey failed 0x%x\n", -n);
goto bail1;
}
kp = (mbedtls_ecp_keypair *)&ecdsa;
n = mbedtls_ecdh_get_params(ctx->u.ctx_ecdh, kp,
(mbedtls_ecdh_side)side);
if (n) {
lwsl_err("mbedtls_ecdh_get_params failed 0x%x\n", -n);
goto bail1;
}
/*
* we need to capture the individual element BIGNUMs into
* lws_gencrypto_keyelem, so they can be serialized, used in jwk etc
*/
mpi[0] = &kp->Q.X;
mpi[1] = &kp->d;
mpi[2] = &kp->Q.Y;
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)
goto bail1;
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;
if (mbedtls_mpi_write_binary(mpi[n - 1], el[n].buf,
curve->key_bytes))
goto bail2;
}
mbedtls_ecdsa_free(&ecdsa);
return 0;
bail2:
for (n = 0; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++)
if (el[n].buf)
lws_free_set_NULL(el[n].buf);
bail1:
mbedtls_ecdsa_free(&ecdsa);
lws_free_set_NULL(ctx->u.ctx_ecdh);
return -1;
}
int
lws_genecdsa_new_keypair(struct lws_genec_ctx *ctx, const char *curve_name,
struct lws_gencrypto_keyelem *el)
{
const struct lws_ec_curves *curve;
mbedtls_ecp_keypair *kp;
mbedtls_mpi *mpi[3];
int n;
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
curve = lws_genec_curve(ctx->curve_table, curve_name);
if (!curve) {
lwsl_err("%s: curve '%s' not supported\n",
__func__, curve_name);
return -22;
}
//mbedtls_ecdsa_init(ctx->u.ctx_ecdsa);
n = mbedtls_ecdsa_genkey(ctx->u.ctx_ecdsa, (mbedtls_ecp_group_id)curve->tls_lib_nid,
lws_gencrypto_mbedtls_rngf, ctx->context);
if (n) {
lwsl_err("mbedtls_ecdsa_genkey failed 0x%x\n", -n);
goto bail1;
}
/*
* we need to capture the individual element BIGNUMs into
* lws_gencrypto_keyelems, so they can be serialized, used in jwk etc
*/
kp = (mbedtls_ecp_keypair *)ctx->u.ctx_ecdsa;
mpi[0] = &kp->Q.X;
mpi[1] = &kp->d;
mpi[2] = &kp->Q.Y;
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)
goto bail1;
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;
if (mbedtls_mpi_write_binary(mpi[n - 1], el[n].buf, el[n].len)) {
lwsl_err("%s: mbedtls_mpi_write_binary failed\n", __func__);
goto bail2;
}
}
return 0;
bail2:
for (n = 0; n < LWS_GENCRYPTO_EC_KEYEL_COUNT; n++)
if (el[n].buf)
lws_free_set_NULL(el[n].buf);
bail1:
lws_free_set_NULL(ctx->u.ctx_ecdsa);
return -1;
}
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 n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
size_t hlen = lws_genhash_size(hash_type);
mbedtls_mpi mpi_r, mpi_s;
size_t slen = sig_len;
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
/*
* 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.
*/
mbedtls_mpi_init(&mpi_r);
mbedtls_mpi_init(&mpi_s);
n = mbedtls_ecdsa_sign(&ctx->u.ctx_ecdsa->grp, &mpi_r, &mpi_s,
&ctx->u.ctx_ecdsa->d, in, hlen,
lws_gencrypto_mbedtls_rngf, ctx->context);
if (n) {
lwsl_err("%s: mbedtls_ecdsa_sign failed: -0x%x\n",
__func__, -n);
goto bail2;
}
if (mbedtls_mpi_write_binary(&mpi_r, sig, (unsigned int)keybytes))
goto bail2;
mbedtls_mpi_free(&mpi_r);
if (mbedtls_mpi_write_binary(&mpi_s, sig + keybytes, (unsigned int)keybytes))
goto bail1;
mbedtls_mpi_free(&mpi_s);
return (int)slen;
bail2:
mbedtls_mpi_free(&mpi_r);
bail1:
mbedtls_mpi_free(&mpi_s);
return -3;
}
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 n, keybytes = lws_gencrypto_bits_to_bytes(keybits);
size_t hlen = lws_genhash_size(hash_type);
mbedtls_mpi mpi_r, mpi_s;
if (ctx->genec_alg != LEGENEC_ECDSA)
return -1;
if ((int)sig_len != keybytes * 2)
return -1;
/*
* 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.
*/
mbedtls_mpi_init(&mpi_r);
mbedtls_mpi_init(&mpi_s);
if (mbedtls_mpi_read_binary(&mpi_r, sig, (unsigned int)keybytes))
return -1;
if (mbedtls_mpi_read_binary(&mpi_s, sig + keybytes, (unsigned int)keybytes))
goto bail1;
n = mbedtls_ecdsa_verify(&ctx->u.ctx_ecdsa->grp, in, hlen,
&ctx->u.ctx_ecdsa->Q, &mpi_r, &mpi_s);
mbedtls_mpi_free(&mpi_s);
mbedtls_mpi_free(&mpi_r);
if (n) {
lwsl_err("%s: mbedtls_ecdsa_verify failed: -0x%x\n",
__func__, -n);
goto bail;
}
return 0;
bail1:
mbedtls_mpi_free(&mpi_r);
bail:
return -3;
}
int
lws_genecdh_compute_shared_secret(struct lws_genec_ctx *ctx, uint8_t *ss,
int *ss_len)
{
int n;
size_t st;
if (mbedtls_ecp_check_pubkey(&ctx->u.ctx_ecdh->grp, &ctx->u.ctx_ecdh->Q) ||
mbedtls_ecp_check_pubkey(&ctx->u.ctx_ecdh->grp, &ctx->u.ctx_ecdh->Qp)) {
lwsl_err("%s: both sides must be set up\n", __func__);
return -1;
}
n = mbedtls_ecdh_calc_secret(ctx->u.ctx_ecdh, &st, ss, (size_t)*ss_len,
lws_gencrypto_mbedtls_rngf, ctx->context);
if (n)
return -1;
*ss_len = (int)st;
return 0;
}