summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordoug <doug@openbsd.org>2017-08-11 05:06:34 +0000
committerdoug <doug@openbsd.org>2017-08-11 05:06:34 +0000
commit6a5d314d73f06b5d5d1f2fab8b24a06956d626db (patch)
tree94edcb35c86c4e3eb871271f42aa3438e81f85b8
parentrefuse to a private keys when its corresponding .pub key does not (diff)
downloadwireguard-openbsd-6a5d314d73f06b5d5d1f2fab8b24a06956d626db.tar.xz
wireguard-openbsd-6a5d314d73f06b5d5d1f2fab8b24a06956d626db.zip
Rewrite the ECPointFormats TLS extension handling using CBB/CBS and the
new extension framework. input + ok jsing@
-rw-r--r--lib/libssl/ssl_locl.h5
-rw-r--r--lib/libssl/ssl_tlsext.c108
-rw-r--r--lib/libssl/ssl_tlsext.h9
-rw-r--r--lib/libssl/t1_lib.c150
-rw-r--r--regress/lib/libssl/tlsext/tlsexttest.c471
5 files changed, 595 insertions, 148 deletions
diff --git a/lib/libssl/ssl_locl.h b/lib/libssl/ssl_locl.h
index 8ef2d014021..c11c5899e37 100644
--- a/lib/libssl/ssl_locl.h
+++ b/lib/libssl/ssl_locl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_locl.h,v 1.184 2017/08/10 17:18:38 jsing Exp $ */
+/* $OpenBSD: ssl_locl.h,v 1.185 2017/08/11 05:06:34 doug Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -1380,6 +1380,9 @@ int SSL_state_func_code(int _state);
#define SSLerrorx(r) ERR_PUT_error(ERR_LIB_SSL,(0xfff),(r),__FILE__,__LINE__)
void SSL_error_internal(const SSL *s, int r, char *f, int l);
+void tls1_get_formatlist(SSL *s, int client_formats, const uint8_t **pformats,
+ size_t *pformatslen);
+
__END_HIDDEN_DECLS
#endif
diff --git a/lib/libssl/ssl_tlsext.c b/lib/libssl/ssl_tlsext.c
index 400c69fa87e..e8723b502cd 100644
--- a/lib/libssl/ssl_tlsext.c
+++ b/lib/libssl/ssl_tlsext.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_tlsext.c,v 1.3 2017/07/24 17:39:43 jsing Exp $ */
+/* $OpenBSD: ssl_tlsext.c,v 1.4 2017/08/11 05:06:34 doug Exp $ */
/*
* Copyright (c) 2016, 2017 Joel Sing <jsing@openbsd.org>
*
@@ -21,6 +21,103 @@
#include "ssl_tlsext.h"
/*
+ * Supported Point Formats Extension - RFC 4492 section 5.1.2
+ */
+static int
+tlsext_ecpf_build(SSL *s, CBB *cbb)
+{
+ CBB ecpf;
+ size_t formats_len;
+ const uint8_t *formats;
+
+ tls1_get_formatlist(s, 0, &formats, &formats_len);
+
+ if (formats_len == 0) {
+ SSLerror(s, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ if (!CBB_add_u8_length_prefixed(cbb, &ecpf))
+ return 0;
+ if (!CBB_add_bytes(&ecpf, formats, formats_len))
+ return 0;
+ if (!CBB_flush(cbb))
+ return 0;
+
+ return 1;
+}
+
+static int
+tlsext_ecpf_parse(SSL *s, CBS *cbs, int *alert)
+{
+ CBS ecpf;
+
+ if (!CBS_get_u8_length_prefixed(cbs, &ecpf))
+ goto err;
+ if (CBS_len(&ecpf) == 0)
+ goto err;
+ if (CBS_len(cbs) != 0)
+ goto err;
+
+ /* Must contain uncompressed (0) */
+ if (!CBS_contains_zero_byte(&ecpf)) {
+ SSLerror(s, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST);
+ goto err;
+ }
+
+ if (!s->internal->hit) {
+ if (!CBS_stow(&ecpf, &(SSI(s)->tlsext_ecpointformatlist),
+ &(SSI(s)->tlsext_ecpointformatlist_length)))
+ goto err;
+ }
+
+ return 1;
+
+ err:
+ *alert = TLS1_AD_INTERNAL_ERROR;
+ return 0;
+}
+
+int
+tlsext_ecpf_clienthello_needs(SSL *s)
+{
+ return ssl_has_ecc_ciphers(s);
+}
+
+int
+tlsext_ecpf_clienthello_build(SSL *s, CBB *cbb)
+{
+ return tlsext_ecpf_build(s, cbb);
+}
+
+int
+tlsext_ecpf_clienthello_parse(SSL *s, CBS *cbs, int *alert)
+{
+ return tlsext_ecpf_parse(s, cbs, alert);
+}
+
+int
+tlsext_ecpf_serverhello_needs(SSL *s)
+{
+ if (s->version == DTLS1_VERSION)
+ return 0;
+
+ return ssl_using_ecc_cipher(s);
+}
+
+int
+tlsext_ecpf_serverhello_build(SSL *s, CBB *cbb)
+{
+ return tlsext_ecpf_build(s, cbb);
+}
+
+int
+tlsext_ecpf_serverhello_parse(SSL *s, CBS *cbs, int *alert)
+{
+ return tlsext_ecpf_parse(s, cbs, alert);
+}
+
+/*
* Renegotiation Indication - RFC 5746.
*/
int
@@ -313,6 +410,15 @@ static struct tls_extension tls_extensions[] = {
.serverhello_build = tlsext_ri_serverhello_build,
.serverhello_parse = tlsext_ri_serverhello_parse,
},
+ {
+ .type = TLSEXT_TYPE_ec_point_formats,
+ .clienthello_needs = tlsext_ecpf_clienthello_needs,
+ .clienthello_build = tlsext_ecpf_clienthello_build,
+ .clienthello_parse = tlsext_ecpf_clienthello_parse,
+ .serverhello_needs = tlsext_ecpf_serverhello_needs,
+ .serverhello_build = tlsext_ecpf_serverhello_build,
+ .serverhello_parse = tlsext_ecpf_serverhello_parse,
+ },
};
#define N_TLS_EXTENSIONS (sizeof(tls_extensions) / sizeof(*tls_extensions))
diff --git a/lib/libssl/ssl_tlsext.h b/lib/libssl/ssl_tlsext.h
index 4b0194861a0..67a9cfb6889 100644
--- a/lib/libssl/ssl_tlsext.h
+++ b/lib/libssl/ssl_tlsext.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_tlsext.h,v 1.2 2017/07/24 17:10:31 jsing Exp $ */
+/* $OpenBSD: ssl_tlsext.h,v 1.3 2017/08/11 05:06:34 doug Exp $ */
/*
* Copyright (c) 2016, 2017 Joel Sing <jsing@openbsd.org>
*
@@ -29,6 +29,13 @@ int tlsext_sni_serverhello_needs(SSL *s);
int tlsext_sni_serverhello_build(SSL *s, CBB *cbb);
int tlsext_sni_serverhello_parse(SSL *s, CBS *cbs, int *alert);
+int tlsext_ecpf_clienthello_needs(SSL *s);
+int tlsext_ecpf_clienthello_build(SSL *s, CBB *cbb);
+int tlsext_ecpf_clienthello_parse(SSL *s, CBS *cbs, int *alert);
+int tlsext_ecpf_serverhello_needs(SSL *s);
+int tlsext_ecpf_serverhello_build(SSL *s, CBB *cbb);
+int tlsext_ecpf_serverhello_parse(SSL *s, CBS *cbs, int *alert);
+
int tlsext_clienthello_build(SSL *s, CBB *cbb);
int tlsext_clienthello_parse_one(SSL *s, CBS *cbs, uint16_t tlsext_type,
int *alert);
diff --git a/lib/libssl/t1_lib.c b/lib/libssl/t1_lib.c
index 42fd18fe2d6..2e90d3e9df1 100644
--- a/lib/libssl/t1_lib.c
+++ b/lib/libssl/t1_lib.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: t1_lib.c,v 1.124 2017/08/10 17:18:38 jsing Exp $ */
+/* $OpenBSD: t1_lib.c,v 1.125 2017/08/11 05:06:34 doug Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -342,7 +342,7 @@ tls1_ec_nid2curve_id(int nid)
* the client/session formats. Otherwise return the custom format list if one
* exists, or the default formats if a custom list has not been specified.
*/
-static void
+void
tls1_get_formatlist(SSL *s, int client_formats, const uint8_t **pformats,
size_t *pformatslen)
{
@@ -699,34 +699,11 @@ ssl_add_clienthello_tlsext(SSL *s, unsigned char *p, unsigned char *limit)
ret += len;
if (using_ecc) {
- size_t curveslen, formatslen, lenmax;
+ size_t curveslen, lenmax;
const uint16_t *curves;
- const uint8_t *formats;
int i;
/*
- * Add TLS extension ECPointFormats to the ClientHello message.
- */
- tls1_get_formatlist(s, 0, &formats, &formatslen);
-
- if ((size_t)(limit - ret) < 5)
- return NULL;
-
- lenmax = limit - ret - 5;
- if (formatslen > lenmax)
- return NULL;
- if (formatslen > 255) {
- SSLerror(s, ERR_R_INTERNAL_ERROR);
- return NULL;
- }
-
- s2n(TLSEXT_TYPE_ec_point_formats, ret);
- s2n(formatslen + 1, ret);
- *(ret++) = (unsigned char)formatslen;
- memcpy(ret, formats, formatslen);
- ret += formatslen;
-
- /*
* Add TLS extension EllipticCurves to the ClientHello message.
*/
tls1_get_curvelist(s, 0, &curves, &curveslen);
@@ -931,14 +908,12 @@ skip_ext:
unsigned char *
ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned char *limit)
{
- int using_ecc, extdatalen = 0;
+ int extdatalen = 0;
unsigned char *ret = p;
int next_proto_neg_seen;
size_t len;
CBB cbb;
- using_ecc = ssl_using_ecc_cipher(s);
-
ret += 2;
if (ret >= limit)
return NULL; /* this really never occurs, but ... */
@@ -957,33 +932,6 @@ ssl_add_serverhello_tlsext(SSL *s, unsigned char *p, unsigned char *limit)
return NULL;
ret += len;
- if (using_ecc && s->version != DTLS1_VERSION) {
- const unsigned char *formats;
- size_t formatslen, lenmax;
-
- /*
- * Add TLS extension ECPointFormats to the ServerHello message.
- */
- tls1_get_formatlist(s, 0, &formats, &formatslen);
-
- if ((size_t)(limit - ret) < 5)
- return NULL;
-
- lenmax = limit - ret - 5;
- if (formatslen > lenmax)
- return NULL;
- if (formatslen > 255) {
- SSLerror(s, ERR_R_INTERNAL_ERROR);
- return NULL;
- }
-
- s2n(TLSEXT_TYPE_ec_point_formats, ret);
- s2n(formatslen + 1, ret);
- *(ret++) = (unsigned char)formatslen;
- memcpy(ret, formats, formatslen);
- ret += formatslen;
- }
-
/*
* Currently the server should not respond with a SupportedCurves
* extension.
@@ -1194,38 +1142,7 @@ ssl_parse_clienthello_tlsext(SSL *s, unsigned char **p, unsigned char *d,
if (!tlsext_clienthello_parse_one(s, &cbs, type, al))
return 0;
- if (type == TLSEXT_TYPE_ec_point_formats &&
- s->version != DTLS1_VERSION) {
- unsigned char *sdata = data;
- size_t formatslen;
- uint8_t *formats;
-
- if (size < 1) {
- *al = TLS1_AD_DECODE_ERROR;
- return 0;
- }
- formatslen = *(sdata++);
- if (formatslen != size - 1) {
- *al = TLS1_AD_DECODE_ERROR;
- return 0;
- }
-
- if (!s->internal->hit) {
- free(SSI(s)->tlsext_ecpointformatlist);
- SSI(s)->tlsext_ecpointformatlist = NULL;
- SSI(s)->tlsext_ecpointformatlist_length = 0;
-
- if ((formats = reallocarray(NULL, formatslen,
- sizeof(uint8_t))) == NULL) {
- *al = TLS1_AD_INTERNAL_ERROR;
- return 0;
- }
- memcpy(formats, sdata, formatslen);
- SSI(s)->tlsext_ecpointformatlist = formats;
- SSI(s)->tlsext_ecpointformatlist_length =
- formatslen;
- }
- } else if (type == TLSEXT_TYPE_elliptic_curves &&
+ if (type == TLSEXT_TYPE_elliptic_curves &&
s->version != DTLS1_VERSION) {
unsigned char *sdata = data;
size_t curveslen, i;
@@ -1510,39 +1427,7 @@ ssl_parse_serverhello_tlsext(SSL *s, unsigned char **p, size_t n, int *al)
if (!tlsext_serverhello_parse_one(s, &cbs, type, al))
return 0;
- if (type == TLSEXT_TYPE_ec_point_formats &&
- s->version != DTLS1_VERSION) {
- unsigned char *sdata = data;
- size_t formatslen;
- uint8_t *formats;
-
- if (size < 1) {
- *al = TLS1_AD_DECODE_ERROR;
- return 0;
- }
- formatslen = *(sdata++);
- if (formatslen != size - 1) {
- *al = TLS1_AD_DECODE_ERROR;
- return 0;
- }
-
- if (!s->internal->hit) {
- free(SSI(s)->tlsext_ecpointformatlist);
- SSI(s)->tlsext_ecpointformatlist = NULL;
- SSI(s)->tlsext_ecpointformatlist_length = 0;
-
- if ((formats = reallocarray(NULL, formatslen,
- sizeof(uint8_t))) == NULL) {
- *al = TLS1_AD_INTERNAL_ERROR;
- return 0;
- }
- memcpy(formats, sdata, formatslen);
- SSI(s)->tlsext_ecpointformatlist = formats;
- SSI(s)->tlsext_ecpointformatlist_length =
- formatslen;
- }
- }
- else if (type == TLSEXT_TYPE_session_ticket) {
+ if (type == TLSEXT_TYPE_session_ticket) {
if (s->internal->tls_session_ticket_ext_cb &&
!s->internal->tls_session_ticket_ext_cb(s, data, size, s->internal->tls_session_ticket_ext_cb_arg)) {
*al = TLS1_AD_INTERNAL_ERROR;
@@ -1779,29 +1664,6 @@ ssl_check_serverhello_tlsext(SSL *s)
int ret = SSL_TLSEXT_ERR_NOACK;
int al = SSL_AD_UNRECOGNIZED_NAME;
- /* If we are client and using an elliptic curve cryptography cipher
- * suite, then if server returns an EC point formats lists extension
- * it must contain uncompressed.
- */
- if (ssl_using_ecc_cipher(s) &&
- s->internal->tlsext_ecpointformatlist != NULL &&
- s->internal->tlsext_ecpointformatlist_length > 0) {
- /* we are using an ECC cipher */
- size_t i;
- unsigned char *list;
- int found_uncompressed = 0;
- list = SSI(s)->tlsext_ecpointformatlist;
- for (i = 0; i < SSI(s)->tlsext_ecpointformatlist_length; i++) {
- if (*(list++) == TLSEXT_ECPOINTFORMAT_uncompressed) {
- found_uncompressed = 1;
- break;
- }
- }
- if (!found_uncompressed) {
- SSLerror(s, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST);
- return -1;
- }
- }
ret = SSL_TLSEXT_ERR_OK;
if (s->ctx != NULL && s->ctx->internal->tlsext_servername_callback != 0)
diff --git a/regress/lib/libssl/tlsext/tlsexttest.c b/regress/lib/libssl/tlsext/tlsexttest.c
index 792ccfe706e..5a7a34c56c0 100644
--- a/regress/lib/libssl/tlsext/tlsexttest.c
+++ b/regress/lib/libssl/tlsext/tlsexttest.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tlsexttest.c,v 1.3 2017/07/24 17:42:14 jsing Exp $ */
+/* $OpenBSD: tlsexttest.c,v 1.4 2017/08/11 05:06:34 doug Exp $ */
/*
* Copyright (c) 2017 Joel Sing <jsing@openbsd.org>
*
@@ -33,6 +33,23 @@ hexdump(const unsigned char *buf, size_t len)
fprintf(stderr, "\n");
}
+static void
+compare_data(const uint8_t *recv, size_t recv_len, const uint8_t *expect,
+ size_t expect_len)
+{
+ fprintf(stderr, "received:\n");
+ hexdump(recv, recv_len);
+
+ fprintf(stderr, "test data:\n");
+ hexdump(expect, expect_len);
+}
+
+#define FAIL(msg, ...) \
+do { \
+ fprintf(stderr, "[%s:%d] FAIL: ", __FILE__, __LINE__); \
+ fprintf(stderr, msg, ##__VA_ARGS__); \
+} while(0)
+
/*
* Renegotiation Indication - RFC 5746.
*/
@@ -522,6 +539,455 @@ test_tlsext_sni_serverhello(void)
return (failure);
}
+/*
+ * ECPointFormats - RFC 4492 section 5.1.2 (Supported Point Formats).
+ *
+ * Examples are from the RFC. Both client and server have the same build and
+ * parse but the needs differ.
+ */
+
+static uint8_t tlsext_ecpf_hello_uncompressed_val[] = {
+ TLSEXT_ECPOINTFORMAT_uncompressed
+};
+static uint8_t tlsext_ecpf_hello_uncompressed[] = {
+ 0x01,
+ 0x00 /* TLSEXT_ECPOINTFORMAT_uncompressed */
+};
+
+static uint8_t tlsext_ecpf_hello_prime[] = {
+ 0x01,
+ 0x01 /* TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime */
+};
+
+static uint8_t tlsext_ecpf_hello_prefer_order_val[] = {
+ TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime,
+ TLSEXT_ECPOINTFORMAT_uncompressed,
+ TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2
+};
+static uint8_t tlsext_ecpf_hello_prefer_order[] = {
+ 0x03,
+ 0x01, /* TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime */
+ 0x00, /* TLSEXT_ECPOINTFORMAT_uncompressed */
+ 0x02 /* TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2 */
+};
+
+static int
+test_tlsext_ecpf_clienthello(void)
+{
+ uint8_t *data = NULL;
+ SSL_CTX *ssl_ctx = NULL;
+ SSL *ssl = NULL;
+ size_t dlen;
+ int failure, alert;
+ CBB cbb;
+ CBS cbs;
+
+ failure = 1;
+
+ CBB_init(&cbb, 0);
+
+ if ((ssl_ctx = SSL_CTX_new(TLS_client_method())) == NULL)
+ errx(1, "failed to create SSL_CTX");
+ if ((ssl = SSL_new(ssl_ctx)) == NULL)
+ errx(1, "failed to create SSL");
+
+ /*
+ * Default ciphers include EC so we need it by default.
+ */
+ if (!tlsext_ecpf_clienthello_needs(ssl)) {
+ FAIL("clienthello should need ECPointFormats for default "
+ "ciphers\n");
+ goto err;
+ }
+
+ /*
+ * Exclude EC cipher suites so we can test not including it.
+ */
+ if (!SSL_set_cipher_list(ssl, "ALL:!ECDHE:!ECDH")) {
+ FAIL("clienthello should be able to set cipher list\n");
+ goto err;
+ }
+ if (tlsext_ecpf_clienthello_needs(ssl)) {
+ FAIL("clienthello should not need ECPointFormats\n");
+ goto err;
+ }
+
+ /*
+ * Use libtls default for the rest of the testing
+ */
+ if (!SSL_set_cipher_list(ssl, "TLSv1.2+AEAD+ECDHE")) {
+ FAIL("clienthello should be able to set cipher list\n");
+ goto err;
+ }
+ if (!tlsext_ecpf_clienthello_needs(ssl)) {
+ FAIL("clienthello should need ECPointFormats\n");
+ goto err;
+ }
+
+ /*
+ * The default ECPointFormats should only have uncompressed
+ */
+ if ((ssl->session = SSL_SESSION_new()) == NULL)
+ errx(1, "failed to create session");
+
+ if (!tlsext_ecpf_clienthello_build(ssl, &cbb)) {
+ FAIL("clienthello failed to build ECPointFormats\n");
+ goto err;
+ }
+
+ if (!CBB_finish(&cbb, &data, &dlen))
+ errx(1, "failed to finish CBB");
+
+ if (dlen != sizeof(tlsext_ecpf_hello_uncompressed)) {
+ FAIL("got clienthello ECPointFormats with length %zu, "
+ "want length %zu\n", dlen,
+ sizeof(tlsext_ecpf_hello_uncompressed));
+ compare_data(data, dlen, tlsext_ecpf_hello_uncompressed,
+ sizeof(tlsext_ecpf_hello_uncompressed));
+ goto err;
+ }
+
+ if (memcmp(data, tlsext_ecpf_hello_uncompressed, dlen) != 0) {
+ FAIL("clienthello ECPointFormats differs:\n");
+ compare_data(data, dlen, tlsext_ecpf_hello_uncompressed,
+ sizeof(tlsext_ecpf_hello_uncompressed));
+ goto err;
+ }
+
+ /*
+ * Make sure we can parse the default.
+ */
+ CBB_cleanup(&cbb);
+ CBB_init(&cbb, 0);
+ free(data);
+ data = NULL;
+
+ SSL_SESSION_free(ssl->session);
+ if ((ssl->session = SSL_SESSION_new()) == NULL)
+ errx(1, "failed to create session");
+
+ CBS_init(&cbs, tlsext_ecpf_hello_uncompressed,
+ sizeof(tlsext_ecpf_hello_uncompressed));
+ if (!tlsext_ecpf_clienthello_parse(ssl, &cbs, &alert)) {
+ FAIL("failed to parse clienthello ECPointFormats\n");
+ goto err;
+ }
+
+ if (SSI(ssl)->tlsext_ecpointformatlist_length !=
+ sizeof(tlsext_ecpf_hello_uncompressed_val)) {
+ FAIL("no tlsext_ecpointformats from clienthello "
+ "ECPointFormats\n");
+ goto err;
+ }
+
+ if (memcmp(SSI(ssl)->tlsext_ecpointformatlist,
+ tlsext_ecpf_hello_uncompressed_val,
+ sizeof(tlsext_ecpf_hello_uncompressed_val)) != 0) {
+ FAIL("clienthello had an incorrect ECPointFormats entry\n");
+ goto err;
+ }
+
+ /*
+ * Test with a custom order.
+ */
+ CBB_cleanup(&cbb);
+ CBB_init(&cbb, 0);
+ free(data);
+ data = NULL;
+
+ SSL_SESSION_free(ssl->session);
+ if ((ssl->session = SSL_SESSION_new()) == NULL)
+ errx(1, "failed to create session");
+
+ if ((ssl->internal->tlsext_ecpointformatlist = malloc(sizeof(uint8_t) * 3)) == NULL) {
+ FAIL("client could not malloc\n");
+ goto err;
+ }
+ ssl->internal->tlsext_ecpointformatlist[0] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime;
+ ssl->internal->tlsext_ecpointformatlist[1] = TLSEXT_ECPOINTFORMAT_uncompressed;
+ ssl->internal->tlsext_ecpointformatlist[2] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2;
+ ssl->internal->tlsext_ecpointformatlist_length = 3;
+
+ if (!tlsext_ecpf_clienthello_needs(ssl)) {
+ FAIL("clienthello should need ECPointFormats with a custom "
+ "format\n");
+ goto err;
+ }
+
+ if (!tlsext_ecpf_clienthello_build(ssl, &cbb)) {
+ FAIL("clienthello failed to build ECPointFormats\n");
+ goto err;
+ }
+
+ if (!CBB_finish(&cbb, &data, &dlen))
+ errx(1, "failed to finish CBB");
+
+ if (dlen != sizeof(tlsext_ecpf_hello_prefer_order)) {
+ FAIL("got clienthello ECPointFormats with length %zu, "
+ "want length %zu\n", dlen,
+ sizeof(tlsext_ecpf_hello_prefer_order));
+ compare_data(data, dlen, tlsext_ecpf_hello_prefer_order,
+ sizeof(tlsext_ecpf_hello_prefer_order));
+ goto err;
+ }
+
+ if (memcmp(data, tlsext_ecpf_hello_prefer_order, dlen) != 0) {
+ FAIL("clienthello ECPointFormats differs:\n");
+ compare_data(data, dlen, tlsext_ecpf_hello_prefer_order,
+ sizeof(tlsext_ecpf_hello_prefer_order));
+ goto err;
+ }
+
+ /*
+ * Make sure that we can parse this custom order.
+ */
+ CBB_cleanup(&cbb);
+ CBB_init(&cbb, 0);
+ free(data);
+ data = NULL;
+
+ SSL_SESSION_free(ssl->session);
+ if ((ssl->session = SSL_SESSION_new()) == NULL)
+ errx(1, "failed to create session");
+
+ /* Reset the custom list so we go back to the default uncompressed. */
+ free(ssl->internal->tlsext_ecpointformatlist);
+ ssl->internal->tlsext_ecpointformatlist = NULL;
+ ssl->internal->tlsext_ecpointformatlist_length = 0;
+
+ CBS_init(&cbs, tlsext_ecpf_hello_prefer_order,
+ sizeof(tlsext_ecpf_hello_prefer_order));
+ if (!tlsext_ecpf_clienthello_parse(ssl, &cbs, &alert)) {
+ FAIL("failed to parse clienthello ECPointFormats\n");
+ goto err;
+ }
+
+ if (SSI(ssl)->tlsext_ecpointformatlist_length !=
+ sizeof(tlsext_ecpf_hello_prefer_order_val)) {
+ FAIL("no tlsext_ecpointformats from clienthello "
+ "ECPointFormats\n");
+ goto err;
+ }
+
+ if (memcmp(SSI(ssl)->tlsext_ecpointformatlist,
+ tlsext_ecpf_hello_prefer_order_val,
+ sizeof(tlsext_ecpf_hello_prefer_order_val)) != 0) {
+ FAIL("clienthello had an incorrect ECPointFormats entry\n");
+ goto err;
+ }
+
+
+ failure = 0;
+
+ err:
+ CBB_cleanup(&cbb);
+ SSL_CTX_free(ssl_ctx);
+ SSL_free(ssl);
+ free(data);
+
+ return (failure);
+}
+
+
+static int
+test_tlsext_ecpf_serverhello(void)
+{
+ uint8_t *data = NULL;
+ SSL_CTX *ssl_ctx = NULL;
+ SSL *ssl = NULL;
+ size_t dlen;
+ int failure, alert;
+ CBB cbb;
+ CBS cbs;
+
+ failure = 1;
+
+ CBB_init(&cbb, 0);
+
+ if ((ssl_ctx = SSL_CTX_new(TLS_server_method())) == NULL)
+ errx(1, "failed to create SSL_CTX");
+ if ((ssl = SSL_new(ssl_ctx)) == NULL)
+ errx(1, "failed to create SSL");
+
+ if ((ssl->session = SSL_SESSION_new()) == NULL)
+ errx(1, "failed to create session");
+
+ /* Setup the state so we can call needs. */
+ if ((S3I(ssl)->hs.new_cipher =
+ ssl3_get_cipher_by_id(TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305))
+ == NULL) {
+ FAIL("serverhello cannot find cipher\n");
+ goto err;
+ }
+ if ((SSI(ssl)->tlsext_ecpointformatlist = malloc(sizeof(uint8_t)))
+ == NULL) {
+ FAIL("server could not malloc\n");
+ goto err;
+ }
+ SSI(ssl)->tlsext_ecpointformatlist[0] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime;
+ SSI(ssl)->tlsext_ecpointformatlist_length = 1;
+
+ if (!tlsext_ecpf_serverhello_needs(ssl)) {
+ FAIL("serverhello should need ECPointFormats now\n");
+ goto err;
+ }
+
+ /*
+ * The server will ignore the session list and use either a custom
+ * list or the default (uncompressed).
+ */
+ if (!tlsext_ecpf_serverhello_build(ssl, &cbb)) {
+ FAIL("serverhello failed to build ECPointFormats\n");
+ goto err;
+ }
+
+ if (!CBB_finish(&cbb, &data, &dlen))
+ errx(1, "failed to finish CBB");
+
+ if (dlen != sizeof(tlsext_ecpf_hello_uncompressed)) {
+ FAIL("got serverhello ECPointFormats with length %zu, "
+ "want length %zu\n", dlen,
+ sizeof(tlsext_ecpf_hello_uncompressed));
+ compare_data(data, dlen, tlsext_ecpf_hello_uncompressed,
+ sizeof(tlsext_ecpf_hello_uncompressed));
+ goto err;
+ }
+
+ if (memcmp(data, tlsext_ecpf_hello_uncompressed, dlen) != 0) {
+ FAIL("serverhello ECPointFormats differs:\n");
+ compare_data(data, dlen, tlsext_ecpf_hello_uncompressed,
+ sizeof(tlsext_ecpf_hello_uncompressed));
+ goto err;
+ }
+
+ /*
+ * Cannot parse a non-default list without at least uncompressed.
+ */
+ CBB_cleanup(&cbb);
+ CBB_init(&cbb, 0);
+ free(data);
+ data = NULL;
+
+ SSL_SESSION_free(ssl->session);
+ if ((ssl->session = SSL_SESSION_new()) == NULL)
+ errx(1, "failed to create session");
+
+ CBS_init(&cbs, tlsext_ecpf_hello_prime,
+ sizeof(tlsext_ecpf_hello_prime));
+ if (tlsext_ecpf_serverhello_parse(ssl, &cbs, &alert)) {
+ FAIL("must include uncompressed in serverhello ECPointFormats\n");
+ goto err;
+ }
+
+ /*
+ * Test with a custom order that replaces the default uncompressed.
+ */
+ CBB_cleanup(&cbb);
+ CBB_init(&cbb, 0);
+ free(data);
+ data = NULL;
+
+ SSL_SESSION_free(ssl->session);
+ if ((ssl->session = SSL_SESSION_new()) == NULL)
+ errx(1, "failed to create session");
+
+ /* Add a session list even though it will be ignored. */
+ if ((SSI(ssl)->tlsext_ecpointformatlist = malloc(sizeof(uint8_t)))
+ == NULL) {
+ FAIL("server could not malloc\n");
+ goto err;
+ }
+ SSI(ssl)->tlsext_ecpointformatlist[0] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2;
+ SSI(ssl)->tlsext_ecpointformatlist_length = 1;
+
+ /* Replace the default list with a custom one. */
+ if ((ssl->internal->tlsext_ecpointformatlist = malloc(sizeof(uint8_t) * 3)) == NULL) {
+ FAIL("server could not malloc\n");
+ goto err;
+ }
+ ssl->internal->tlsext_ecpointformatlist[0] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime;
+ ssl->internal->tlsext_ecpointformatlist[1] = TLSEXT_ECPOINTFORMAT_uncompressed;
+ ssl->internal->tlsext_ecpointformatlist[2] = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_char2;
+ ssl->internal->tlsext_ecpointformatlist_length = 3;
+
+ if (!tlsext_ecpf_serverhello_needs(ssl)) {
+ FAIL("serverhello should need ECPointFormats\n");
+ goto err;
+ }
+
+ if (!tlsext_ecpf_serverhello_build(ssl, &cbb)) {
+ FAIL("serverhello failed to build ECPointFormats\n");
+ goto err;
+ }
+
+ if (!CBB_finish(&cbb, &data, &dlen))
+ errx(1, "failed to finish CBB");
+
+ if (dlen != sizeof(tlsext_ecpf_hello_prefer_order)) {
+ FAIL("got serverhello ECPointFormats with length %zu, "
+ "want length %zu\n", dlen,
+ sizeof(tlsext_ecpf_hello_prefer_order));
+ compare_data(data, dlen, tlsext_ecpf_hello_prefer_order,
+ sizeof(tlsext_ecpf_hello_prefer_order));
+ goto err;
+ }
+
+ if (memcmp(data, tlsext_ecpf_hello_prefer_order, dlen) != 0) {
+ FAIL("serverhello ECPointFormats differs:\n");
+ compare_data(data, dlen, tlsext_ecpf_hello_prefer_order,
+ sizeof(tlsext_ecpf_hello_prefer_order));
+ goto err;
+ }
+
+ /*
+ * Should be able to parse the custom list into a session list.
+ */
+ CBB_cleanup(&cbb);
+ CBB_init(&cbb, 0);
+ free(data);
+ data = NULL;
+
+ SSL_SESSION_free(ssl->session);
+ if ((ssl->session = SSL_SESSION_new()) == NULL)
+ errx(1, "failed to create session");
+
+ /* Reset back to the default (uncompressed) */
+ free(ssl->internal->tlsext_ecpointformatlist);
+ ssl->internal->tlsext_ecpointformatlist = NULL;
+ ssl->internal->tlsext_ecpointformatlist_length = 0;
+
+ CBS_init(&cbs, tlsext_ecpf_hello_prefer_order,
+ sizeof(tlsext_ecpf_hello_prefer_order));
+ if (!tlsext_ecpf_serverhello_parse(ssl, &cbs, &alert)) {
+ FAIL("failed to parse serverhello ECPointFormats\n");
+ goto err;
+ }
+
+ if (SSI(ssl)->tlsext_ecpointformatlist_length !=
+ sizeof(tlsext_ecpf_hello_prefer_order_val)) {
+ FAIL("no tlsext_ecpointformats from serverhello "
+ "ECPointFormats\n");
+ goto err;
+ }
+
+ if (memcmp(SSI(ssl)->tlsext_ecpointformatlist,
+ tlsext_ecpf_hello_prefer_order_val,
+ sizeof(tlsext_ecpf_hello_prefer_order_val)) != 0) {
+ FAIL("serverhello had an incorrect ECPointFormats entry\n");
+ goto err;
+ }
+
+ failure = 0;
+
+ err:
+ CBB_cleanup(&cbb);
+ SSL_CTX_free(ssl_ctx);
+ SSL_free(ssl);
+ free(data);
+
+ return (failure);
+}
+
int
main(int argc, char **argv)
{
@@ -535,5 +1001,8 @@ main(int argc, char **argv)
failed |= test_tlsext_sni_clienthello();
failed |= test_tlsext_sni_serverhello();
+ failed |= test_tlsext_ecpf_clienthello();
+ failed |= test_tlsext_ecpf_serverhello();
+
return (failed);
}