+/* Get curves list, if "sess" is set return client curves otherwise
+ * preferred list
+ */
+static void tls1_get_curvelist(SSL *s, int sess,
+ const unsigned char **pcurves,
+ size_t *pcurveslen)
+ {
+ if (sess)
+ {
+ *pcurves = s->session->tlsext_ellipticcurvelist;
+ *pcurveslen = s->session->tlsext_ellipticcurvelist_length;
+ return;
+ }
+ /* For Suite B mode only include P-256, P-384 */
+ switch (tls1_suiteb(s))
+ {
+ case SSL_CERT_FLAG_SUITEB_128_LOS:
+ *pcurves = suiteb_curves;
+ *pcurveslen = sizeof(suiteb_curves);
+ break;
+
+ case SSL_CERT_FLAG_SUITEB_128_LOS_ONLY:
+ *pcurves = suiteb_curves;
+ *pcurveslen = 2;
+ break;
+
+ case SSL_CERT_FLAG_SUITEB_192_LOS:
+ *pcurves = suiteb_curves + 2;
+ *pcurveslen = 2;
+ break;
+ default:
+ *pcurves = s->tlsext_ellipticcurvelist;
+ *pcurveslen = s->tlsext_ellipticcurvelist_length;
+ }
+ if (!*pcurves)
+ {
+ *pcurves = eccurves_default;
+ *pcurveslen = sizeof(eccurves_default);
+ }
+ }
+/* Check a curve is one of our preferences */
+int tls1_check_curve(SSL *s, const unsigned char *p, size_t len)
+ {
+ const unsigned char *curves;
+ size_t curveslen, i;
+ unsigned int suiteb_flags = tls1_suiteb(s);
+ if (len != 3 || p[0] != NAMED_CURVE_TYPE)
+ return 0;
+ /* Check curve matches Suite B preferences */
+ if (suiteb_flags)
+ {
+ unsigned long cid = s->s3->tmp.new_cipher->id;
+ if (p[1])
+ return 0;
+ if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
+ {
+ if (p[2] != TLSEXT_curve_P_256)
+ return 0;
+ }
+ else if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
+ {
+ if (p[2] != TLSEXT_curve_P_384)
+ return 0;
+ }
+ else /* Should never happen */
+ return 0;
+ }
+ tls1_get_curvelist(s, 0, &curves, &curveslen);
+ for (i = 0; i < curveslen; i += 2, curves += 2)
+ {
+ if (p[1] == curves[0] && p[2] == curves[1])
+ return 1;
+ }
+ return 0;
+ }
+
+/* Return nth shared curve. If nmatch == -1 return number of
+ * matches. For nmatch == -2 return the NID of the curve to use for
+ * an EC tmp key.
+ */
+
+int tls1_shared_curve(SSL *s, int nmatch)
+ {
+ const unsigned char *pref, *supp;
+ size_t preflen, supplen, i, j;
+ int k;
+ /* Can't do anything on client side */
+ if (s->server == 0)
+ return -1;
+ if (nmatch == -2)
+ {
+ if (tls1_suiteb(s))
+ {
+ /* For Suite B ciphersuite determines curve: we
+ * already know these are acceptable due to previous
+ * checks.
+ */
+ unsigned long cid = s->s3->tmp.new_cipher->id;
+ if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
+ return NID_X9_62_prime256v1; /* P-256 */
+ if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
+ return NID_secp384r1; /* P-384 */
+ /* Should never happen */
+ return NID_undef;
+ }
+ /* If not Suite B just return first preference shared curve */
+ nmatch = 0;
+ }
+ tls1_get_curvelist(s, !!(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE),
+ &supp, &supplen);
+ tls1_get_curvelist(s, !(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE),
+ &pref, &preflen);
+ preflen /= 2;
+ supplen /= 2;
+ k = 0;
+ for (i = 0; i < preflen; i++, pref+=2)
+ {
+ const unsigned char *tsupp = supp;
+ for (j = 0; j < supplen; j++, tsupp+=2)
+ {
+ if (pref[0] == tsupp[0] && pref[1] == tsupp[1])
+ {
+ if (nmatch == k)
+ {
+ int id = (pref[0] << 8) | pref[1];
+ return tls1_ec_curve_id2nid(id);
+ }
+ k++;
+ }
+ }
+ }
+ if (nmatch == -1)
+ return k;
+ return 0;
+ }
+
+int tls1_set_curves(unsigned char **pext, size_t *pextlen,
+ int *curves, size_t ncurves)
+ {
+ unsigned char *clist, *p;
+ size_t i;
+ /* Bitmap of curves included to detect duplicates: only works
+ * while curve ids < 32
+ */
+ unsigned long dup_list = 0;
+ clist = OPENSSL_malloc(ncurves * 2);
+ if (!clist)
+ return 0;
+ for (i = 0, p = clist; i < ncurves; i++)
+ {
+ unsigned long idmask;
+ int id;
+ id = tls1_ec_nid2curve_id(curves[i]);
+ idmask = 1L << id;
+ if (!id || (dup_list & idmask))
+ {
+ OPENSSL_free(clist);
+ return 0;
+ }
+ dup_list |= idmask;
+ s2n(id, p);
+ }
+ if (*pext)
+ OPENSSL_free(*pext);
+ *pext = clist;
+ *pextlen = ncurves * 2;
+ return 1;
+ }
+
+#define MAX_CURVELIST 25
+
+typedef struct
+ {
+ size_t nidcnt;
+ int nid_arr[MAX_CURVELIST];
+ } nid_cb_st;
+
+static int nid_cb(const char *elem, int len, void *arg)
+ {
+ nid_cb_st *narg = arg;
+ size_t i;
+ int nid;
+ char etmp[20];
+ if (narg->nidcnt == MAX_CURVELIST)
+ return 0;
+ if (len > (int)(sizeof(etmp) - 1))
+ return 0;
+ memcpy(etmp, elem, len);
+ etmp[len] = 0;
+ nid = EC_curve_nist2nid(etmp);
+ if (nid == NID_undef)
+ nid = OBJ_sn2nid(etmp);
+ if (nid == NID_undef)
+ nid = OBJ_ln2nid(etmp);
+ if (nid == NID_undef)
+ return 0;
+ for (i = 0; i < narg->nidcnt; i++)
+ if (narg->nid_arr[i] == nid)
+ return 0;
+ narg->nid_arr[narg->nidcnt++] = nid;
+ return 1;
+ }
+/* Set curves based on a colon separate list */
+int tls1_set_curves_list(unsigned char **pext, size_t *pextlen,
+ const char *str)
+ {
+ nid_cb_st ncb;
+ ncb.nidcnt = 0;
+ if (!CONF_parse_list(str, ':', 1, nid_cb, &ncb))
+ return 0;
+ return tls1_set_curves(pext, pextlen, ncb.nid_arr, ncb.nidcnt);
+ }
+/* For an EC key set TLS id and required compression based on parameters */
+static int tls1_set_ec_id(unsigned char *curve_id, unsigned char *comp_id,
+ EC_KEY *ec)
+ {
+ int is_prime, id;
+ const EC_GROUP *grp;
+ const EC_POINT *pt;
+ const EC_METHOD *meth;
+ if (!ec)
+ return 0;
+ /* Determine if it is a prime field */
+ grp = EC_KEY_get0_group(ec);
+ pt = EC_KEY_get0_public_key(ec);
+ if (!grp || !pt)
+ return 0;
+ meth = EC_GROUP_method_of(grp);
+ if (!meth)
+ return 0;
+ if (EC_METHOD_get_field_type(meth) == NID_X9_62_prime_field)
+ is_prime = 1;
+ else
+ is_prime = 0;
+ /* Determine curve ID */
+ id = EC_GROUP_get_curve_name(grp);
+ id = tls1_ec_nid2curve_id(id);
+ /* If we have an ID set it, otherwise set arbitrary explicit curve */
+ if (id)
+ {
+ curve_id[0] = 0;
+ curve_id[1] = (unsigned char)id;
+ }
+ else
+ {
+ curve_id[0] = 0xff;
+ if (is_prime)
+ curve_id[1] = 0x01;
+ else
+ curve_id[1] = 0x02;
+ }
+ if (comp_id)
+ {
+ if (EC_KEY_get_conv_form(ec) == POINT_CONVERSION_COMPRESSED)
+ {
+ if (is_prime)
+ *comp_id = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime;
+ else
+ *comp_id = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2;
+ }
+ else
+ *comp_id = TLSEXT_ECPOINTFORMAT_uncompressed;
+ }
+ return 1;
+ }
+/* Check an EC key is compatible with extensions */
+static int tls1_check_ec_key(SSL *s,
+ unsigned char *curve_id, unsigned char *comp_id)
+ {
+ const unsigned char *p;
+ size_t plen, i;
+ int j;
+ /* If point formats extension present check it, otherwise everything
+ * is supported (see RFC4492).
+ */
+ if (comp_id && s->session->tlsext_ecpointformatlist)
+ {
+ p = s->session->tlsext_ecpointformatlist;
+ plen = s->session->tlsext_ecpointformatlist_length;
+ for (i = 0; i < plen; i++, p++)
+ {
+ if (*comp_id == *p)
+ break;
+ }
+ if (i == plen)
+ return 0;
+ }
+ if (!curve_id)
+ return 1;
+ /* Check curve is consistent with client and server preferences */
+ for (j = 0; j <= 1; j++)
+ {
+ tls1_get_curvelist(s, j, &p, &plen);
+ for (i = 0; i < plen; i+=2, p+=2)
+ {
+ if (p[0] == curve_id[0] && p[1] == curve_id[1])
+ break;
+ }
+ if (i == plen)
+ return 0;
+ /* For clients can only check sent curve list */
+ if (!s->server)
+ return 1;
+ }
+ return 1;
+ }
+
+/* Check cert parameters compatible with extensions: currently just checks
+ * EC certificates have compatible curves and compression.
+ */
+static int tls1_check_cert_param(SSL *s, X509 *x, int set_ee_md)
+ {
+ unsigned char comp_id, curve_id[2];
+ EVP_PKEY *pkey;
+ int rv;
+ pkey = X509_get_pubkey(x);
+ if (!pkey)
+ return 0;
+ /* If not EC nothing to do */
+ if (pkey->type != EVP_PKEY_EC)
+ {
+ EVP_PKEY_free(pkey);
+ return 1;
+ }
+ rv = tls1_set_ec_id(curve_id, &comp_id, pkey->pkey.ec);
+ EVP_PKEY_free(pkey);
+ if (!rv)
+ return 0;
+ /* Can't check curve_id for client certs as we don't have a
+ * supported curves extension.
+ */
+ rv = tls1_check_ec_key(s, s->server ? curve_id : NULL, &comp_id);
+ if (!rv)
+ return 0;
+ /* Special case for suite B. We *MUST* sign using SHA256+P-256 or
+ * SHA384+P-384, adjust digest if necessary.
+ */
+ if (set_ee_md && tls1_suiteb(s))
+ {
+ int check_md;
+ size_t i;
+ CERT *c = s->cert;
+ if (curve_id[0])
+ return 0;
+ /* Check to see we have necessary signing algorithm */
+ if (curve_id[1] == TLSEXT_curve_P_256)
+ check_md = NID_ecdsa_with_SHA256;
+ else if (curve_id[1] == TLSEXT_curve_P_384)
+ check_md = NID_ecdsa_with_SHA384;
+ else
+ return 0; /* Should never happen */
+ for (i = 0; i < c->shared_sigalgslen; i++)
+ if (check_md == c->shared_sigalgs[i].signandhash_nid)
+ break;
+ if (i == c->shared_sigalgslen)
+ return 0;
+ if (set_ee_md == 2)
+ {
+ if (check_md == NID_ecdsa_with_SHA256)
+ c->pkeys[SSL_PKEY_ECC].digest = EVP_sha256();
+ else
+ c->pkeys[SSL_PKEY_ECC].digest = EVP_sha384();
+ }
+ }
+ return rv;
+ }
+/* Check EC temporary key is compatible with client extensions */
+int tls1_check_ec_tmp_key(SSL *s, unsigned long cid)
+ {
+ unsigned char curve_id[2];
+ EC_KEY *ec = s->cert->ecdh_tmp;
+#ifdef OPENSSL_SSL_DEBUG_BROKEN_PROTOCOL
+ /* Allow any curve: not just those peer supports */
+ if (s->cert->cert_flags & SSL_CERT_FLAG_BROKEN_PROTCOL)
+ return 1;
+#endif
+ /* If Suite B, AES128 MUST use P-256 and AES256 MUST use P-384,
+ * no other curves permitted.
+ */
+ if (tls1_suiteb(s))
+ {
+ /* Curve to check determined by ciphersuite */
+ if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
+ curve_id[1] = TLSEXT_curve_P_256;
+ else if (cid == TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
+ curve_id[1] = TLSEXT_curve_P_384;
+ else
+ return 0;
+ curve_id[0] = 0;
+ /* Check this curve is acceptable */
+ if (!tls1_check_ec_key(s, curve_id, NULL))
+ return 0;
+ /* If auto or setting curve from callback assume OK */
+ if (s->cert->ecdh_tmp_auto || s->cert->ecdh_tmp_cb)
+ return 1;
+ /* Otherwise check curve is acceptable */
+ else
+ {
+ unsigned char curve_tmp[2];
+ if (!ec)
+ return 0;
+ if (!tls1_set_ec_id(curve_tmp, NULL, ec))
+ return 0;
+ if (!curve_tmp[0] || curve_tmp[1] == curve_id[1])
+ return 1;
+ return 0;
+ }
+
+ }
+ if (s->cert->ecdh_tmp_auto)
+ {
+ /* Need a shared curve */
+ if (tls1_shared_curve(s, 0))
+ return 1;
+ else return 0;
+ }
+ if (!ec)
+ {
+ if (s->cert->ecdh_tmp_cb)
+ return 1;
+ else
+ return 0;
+ }
+ if (!tls1_set_ec_id(curve_id, NULL, ec))
+ return 0;
+/* Set this to allow use of invalid curves for testing */
+#if 0
+ return 1;
+#else
+ return tls1_check_ec_key(s, curve_id, NULL);
+#endif
+ }
+