/* * libwebsockets - small server side websockets and web server implementation * * Copyright (C) 2010 - 2019 Andy Green * * 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; }