correct error codes
[openssl.git] / ssl / t1_lib.c
index 7a8dc31203ea47b2864b722611eea4fb59154a8c..e120a87fc2bb78dc3a9f50833606e7baa16ac677 100644 (file)
@@ -307,34 +307,49 @@ int tls1_ec_nid2curve_id(int nid)
                return 0;
                }
        }
-
-/* Return any common values from two lists. One list is used as a 
- * preference list where we return the most preferred match.
+/* Get curves list, if "sess" is set return client curves otherwise
+ * preferred list
  */
-int tls1_shared_list(SSL *s,
-                       const unsigned char *l1, size_t l1len,
-                       const unsigned char *l2, size_t l2len,
-                       int nmatch)
+static void tls1_get_curvelist(SSL *s, int sess,
+                                       const unsigned char **pcurves,
+                                       size_t *pcurveslen)
        {
-       const unsigned char *pref, *supp;
-       size_t preflen, supplen, i, j;
-       int k;
-       l1len /= 2;
-       l2len /= 2;
-       if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE)
+       if (sess)
                {
-               pref = l1;
-               preflen = l1len;
-               supp = l2;
-               supplen = l2len;
+               *pcurves = s->session->tlsext_ellipticcurvelist;
+               *pcurveslen = s->session->tlsext_ellipticcurvelist_length;
                }
        else
                {
-               supp = l1;
-               supplen = l1len;
-               pref = l2;
-               preflen = l2len;
+               *pcurves = s->tlsext_ellipticcurvelist;
+               *pcurveslen = s->tlsext_ellipticcurvelist_length;
+               }
+       /* If not set use default: for now static structure */
+       if (!*pcurves)
+               {
+               *pcurves = eccurves_default;
+               *pcurveslen = sizeof(eccurves_default);
                }
+       }
+
+/* Return nth shared curve. If nmatch == -1 return number of
+ * matches.
+ */
+
+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;
+       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)
                {
@@ -344,50 +359,17 @@ int tls1_shared_list(SSL *s,
                        if (pref[0] == tsupp[0] && pref[1] == tsupp[1])
                                {
                                if (nmatch == k)
-                                       return (pref[0] << 8) | pref[1];
+                                       {
+                                       int id = (pref[0] << 8) | pref[1];
+                                       return tls1_ec_curve_id2nid(id);
+                                       }
                                k++;
                                }
                        }
                }
-       if (nmatch == -1 && k > 0)
-               return k;
-       return -1;
-       }
-
-int tls1_shared_curve(SSL *s, int nmatch)
-       {
-       const unsigned char *l1, *l2;
-       size_t l1len, l2len;
-       int id;
-       /* Can't do anything on client side */
-       if (s->server == 0)
-               return -1;
-       /* Use our preferred curve list, if not set use default */
-       if (s->tlsext_ellipticcurvelist)
-               {
-               l1 = s->tlsext_ellipticcurvelist;
-               l1len = s->tlsext_ellipticcurvelist_length;
-               }
-       else
-               {
-               l1 = eccurves_default;
-               l1len = sizeof(eccurves_default);
-               }
-       /* Use peer preferred curve list, if not set use default */
-       if(s->session->tlsext_ellipticcurvelist)
-               {
-               l2 = s->session->tlsext_ellipticcurvelist;
-               l2len =s->session->tlsext_ellipticcurvelist_length;
-               }
-       else
-               {
-               l2 = eccurves_default;
-               l2len = sizeof(eccurves_default);
-               }
-       id = tls1_shared_list(s, l1, l1len, l2, l2len, nmatch);
        if (nmatch == -1)
-               return id;
-       return tls1_ec_curve_id2nid(id);
+               return k;
+       return 0;
        }
 
 int tls1_set_curves(unsigned char **pext, size_t *pextlen,
@@ -466,6 +448,136 @@ int tls1_set_curves_list(unsigned char **pext, size_t *pextlen,
                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;
+               }
+       /* 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;
+               }
+       return 1;
+       }
+/* Check EC server key is compatible with client extensions */
+int tls1_check_ec_server_key(SSL *s)
+       {
+       int rv;
+       CERT_PKEY *cpk = s->cert->pkeys + SSL_PKEY_ECC;
+       EVP_PKEY *pkey;
+       unsigned char comp_id, curve_id[2];
+       if (!cpk->x509 || !cpk->privatekey)
+               return 0;
+       pkey = X509_get_pubkey(cpk->x509);
+       if (!pkey)
+               return 0;
+       rv = tls1_set_ec_id(curve_id, &comp_id, pkey->pkey.ec);
+       EVP_PKEY_free(pkey);
+       if (!rv)
+               return 0;
+       return tls1_check_ec_key(s, curve_id, &comp_id);
+       }
+/* Check EC temporary key is compatible with client extensions */
+int tls1_check_ec_tmp_key(SSL *s)
+       {
+       unsigned char curve_id[2];
+       EC_KEY *ec = s->cert->ecdh_tmp;
+       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 1;
+       return tls1_check_ec_key(s, curve_id, NULL);
+       }
 
 #endif /* OPENSSL_NO_EC */
 
@@ -685,15 +797,7 @@ unsigned char *ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned cha
 
                /* Add TLS extension EllipticCurves to the ClientHello message */
                plist = s->tlsext_ellipticcurvelist;
-               /* If we have a custom curve list use it otherwise
-                * use default */
-               if (plist)
-                       plistlen = s->tlsext_ellipticcurvelist_length;
-               else
-                       {
-                       plist = eccurves_default;
-                       plistlen = sizeof(eccurves_default);
-                       }
+               tls1_get_curvelist(s, 0, &plist, &plistlen);
 
                if ((lenmax = limit - ret - 6) < 0) return NULL; 
                if (plistlen > (size_t)lenmax) return NULL;
@@ -1071,8 +1175,8 @@ unsigned char *ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned cha
        return ret;
        }
 
-int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al)
-       {
+static int ssl_scan_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n, int *al) 
+       {       
        unsigned short type;
        unsigned short size;
        unsigned short len;
@@ -1557,7 +1661,7 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
                !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION))
                {
                *al = SSL_AD_HANDSHAKE_FAILURE;
-               SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_TLSEXT,
+               SSLerr(SSL_F_SSL_SCAN_CLIENTHELLO_TLSEXT,
                                SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
                return 0;
                }
@@ -1565,6 +1669,23 @@ int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, in
        return 1;
        }
 
+int ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d, int n) 
+       {
+       int al = -1;
+       if (ssl_scan_clienthello_tlsext(s, p, d, n, &al) <= 0) 
+               {
+               ssl3_send_alert(s,SSL3_AL_FATAL,al); 
+               return 0;
+               }
+
+       if (ssl_check_clienthello_tlsext(s) <= 0) 
+               {
+               SSLerr(SSL_F_SSL_PARSE_CLIENTHELLO_TLSEXT,SSL_R_CLIENTHELLO_TLSEXT);
+               return 0;
+               }
+       return 1;
+}
+
 #ifndef OPENSSL_NO_NEXTPROTONEG
 /* ssl_next_proto_validate validates a Next Protocol Negotiation block. No
  * elements of zero length are allowed and the set of elements must exactly fill