From 2c7289aaf0a5e656e3db4ad3e101b1dd3e2dc610 Mon Sep 17 00:00:00 2001 From: doug <> Date: Fri, 11 Aug 2017 05:06:34 +0000 Subject: Rewrite the ECPointFormats TLS extension handling using CBB/CBS and the new extension framework. input + ok jsing@ --- src/lib/libssl/ssl_locl.h | 5 +- src/lib/libssl/ssl_tlsext.c | 108 ++++++- src/lib/libssl/ssl_tlsext.h | 9 +- src/lib/libssl/t1_lib.c | 150 +-------- src/regress/lib/libssl/tlsext/tlsexttest.c | 471 ++++++++++++++++++++++++++++- 5 files changed, 595 insertions(+), 148 deletions(-) (limited to 'src') diff --git a/src/lib/libssl/ssl_locl.h b/src/lib/libssl/ssl_locl.h index 8ef2d01402..c11c5899e3 100644 --- a/src/lib/libssl/ssl_locl.h +++ b/src/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/src/lib/libssl/ssl_tlsext.c b/src/lib/libssl/ssl_tlsext.c index 400c69fa87..e8723b502c 100644 --- a/src/lib/libssl/ssl_tlsext.c +++ b/src/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 * @@ -20,6 +20,103 @@ #include "bytestring.h" #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. */ @@ -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/src/lib/libssl/ssl_tlsext.h b/src/lib/libssl/ssl_tlsext.h index 4b0194861a..67a9cfb688 100644 --- a/src/lib/libssl/ssl_tlsext.h +++ b/src/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 * @@ -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/src/lib/libssl/t1_lib.c b/src/lib/libssl/t1_lib.c index 42fd18fe2d..2e90d3e9df 100644 --- a/src/lib/libssl/t1_lib.c +++ b/src/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,33 +699,10 @@ 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. */ @@ -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/src/regress/lib/libssl/tlsext/tlsexttest.c b/src/regress/lib/libssl/tlsext/tlsexttest.c index 792ccfe706..5a7a34c56c 100644 --- a/src/regress/lib/libssl/tlsext/tlsexttest.c +++ b/src/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 * @@ -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); } -- cgit v1.2.3-55-g6feb