diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl')
97 files changed, 64342 insertions, 0 deletions
diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/CMakeLists.txt b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/CMakeLists.txt new file mode 100644 index 000000000..5b5ea976e --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/CMakeLists.txt @@ -0,0 +1,55 @@ +include_directories(../include) + +add_library( + ssl + + bio_ssl.c + custom_extensions.c + d1_both.c + d1_lib.c + d1_pkt.c + d1_srtp.c + dtls_method.c + dtls_record.c + handshake_client.c + handshake_server.c + s3_both.c + s3_lib.c + s3_pkt.c + ssl_aead_ctx.c + ssl_asn1.c + ssl_buffer.c + ssl_cert.c + ssl_cipher.c + ssl_ecdh.c + ssl_file.c + ssl_lib.c + ssl_privkey.c + ssl_privkey_cc.cc + ssl_session.c + ssl_stat.c + ssl_transcript.c + ssl_x509.c + t1_enc.c + t1_lib.c + tls_method.c + tls_record.c + tls13_both.c + tls13_client.c + tls13_enc.c + tls13_server.c +) + +target_link_libraries(ssl crypto) + +add_executable( + ssl_test + + ssl_test.cc + + $<TARGET_OBJECTS:gtest_main> + $<TARGET_OBJECTS:test_support> +) + +target_link_libraries(ssl_test ssl crypto gtest) +add_dependencies(all_tests ssl_test) diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/bio_ssl.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/bio_ssl.c new file mode 100644 index 000000000..ad8f5d8ff --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/bio_ssl.c @@ -0,0 +1,175 @@ +/* + * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/ssl.h> + +#include <openssl/bio.h> + + +static int ssl_read(BIO *bio, char *out, int outl) { + SSL *ssl = bio->ptr; + if (ssl == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + const int ret = SSL_read(ssl, out, outl); + + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_WANT_READ: + BIO_set_retry_read(bio); + break; + + case SSL_ERROR_WANT_WRITE: + BIO_set_retry_write(bio); + break; + + case SSL_ERROR_WANT_ACCEPT: + BIO_set_retry_special(bio); + bio->retry_reason = BIO_RR_ACCEPT; + break; + + case SSL_ERROR_WANT_CONNECT: + BIO_set_retry_special(bio); + bio->retry_reason = BIO_RR_CONNECT; + break; + + case SSL_ERROR_NONE: + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + default: + break; + } + + return ret; +} + +static int ssl_write(BIO *bio, const char *out, int outl) { + SSL *ssl = bio->ptr; + if (ssl == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + const int ret = SSL_write(ssl, out, outl); + + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_WANT_WRITE: + BIO_set_retry_write(bio); + break; + + case SSL_ERROR_WANT_READ: + BIO_set_retry_read(bio); + break; + + case SSL_ERROR_WANT_CONNECT: + BIO_set_retry_special(bio); + bio->retry_reason = BIO_RR_CONNECT; + break; + + case SSL_ERROR_NONE: + case SSL_ERROR_SYSCALL: + case SSL_ERROR_SSL: + default: + break; + } + + return ret; +} + +static long ssl_ctrl(BIO *bio, int cmd, long num, void *ptr) { + SSL *ssl = bio->ptr; + if (ssl == NULL && cmd != BIO_C_SET_SSL) { + return 0; + } + + switch (cmd) { + case BIO_C_SET_SSL: + bio->shutdown = num; + bio->ptr = ptr; + bio->init = 1; + return 1; + + case BIO_CTRL_GET_CLOSE: + return bio->shutdown; + + case BIO_CTRL_SET_CLOSE: + bio->shutdown = num; + return 1; + + case BIO_CTRL_WPENDING: + return BIO_ctrl(SSL_get_wbio(ssl), cmd, num, ptr); + + case BIO_CTRL_PENDING: + return SSL_pending(ssl); + + case BIO_CTRL_FLUSH: { + BIO_clear_retry_flags(bio); + long ret = BIO_ctrl(SSL_get_wbio(ssl), cmd, num, ptr); + BIO_copy_next_retry(bio); + return ret; + } + + case BIO_CTRL_PUSH: + case BIO_CTRL_POP: + case BIO_CTRL_DUP: + return -1; + + default: + return BIO_ctrl(SSL_get_rbio(ssl), cmd, num, ptr); + } +} + +static int ssl_new(BIO *bio) { + return 1; +} + +static int ssl_free(BIO *bio) { + SSL *ssl = bio->ptr; + + if (ssl == NULL) { + return 1; + } + + SSL_shutdown(ssl); + if (bio->shutdown) { + SSL_free(ssl); + } + + return 1; +} + +static long ssl_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) { + SSL *ssl = bio->ptr; + if (ssl == NULL) { + return 0; + } + + switch (cmd) { + case BIO_CTRL_SET_CALLBACK: + return -1; + + default: + return BIO_callback_ctrl(SSL_get_rbio(ssl), cmd, fp); + } +} + +static const BIO_METHOD ssl_method = { + BIO_TYPE_SSL, "SSL", ssl_write, ssl_read, NULL, + NULL, ssl_ctrl, ssl_new, ssl_free, ssl_callback_ctrl, +}; + +const BIO_METHOD *BIO_f_ssl(void) { return &ssl_method; } + +long BIO_set_ssl(BIO *bio, SSL *ssl, int take_owership) { + return BIO_ctrl(bio, BIO_C_SET_SSL, take_owership, ssl); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/custom_extensions.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/custom_extensions.c new file mode 100644 index 000000000..10fbfc8f0 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/custom_extensions.c @@ -0,0 +1,258 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/stack.h> + +#include "internal.h" + + +void SSL_CUSTOM_EXTENSION_free(SSL_CUSTOM_EXTENSION *custom_extension) { + OPENSSL_free(custom_extension); +} + +static const SSL_CUSTOM_EXTENSION *custom_ext_find( + STACK_OF(SSL_CUSTOM_EXTENSION) *stack, + unsigned *out_index, uint16_t value) { + for (size_t i = 0; i < sk_SSL_CUSTOM_EXTENSION_num(stack); i++) { + const SSL_CUSTOM_EXTENSION *ext = sk_SSL_CUSTOM_EXTENSION_value(stack, i); + if (ext->value == value) { + if (out_index != NULL) { + *out_index = i; + } + return ext; + } + } + + return NULL; +} + +/* default_add_callback is used as the |add_callback| when the user doesn't + * provide one. For servers, it does nothing while, for clients, it causes an + * empty extension to be included. */ +static int default_add_callback(SSL *ssl, unsigned extension_value, + const uint8_t **out, size_t *out_len, + int *out_alert_value, void *add_arg) { + if (ssl->server) { + return 0; + } + *out_len = 0; + return 1; +} + +static int custom_ext_add_hello(SSL_HANDSHAKE *hs, CBB *extensions) { + SSL *const ssl = hs->ssl; + STACK_OF(SSL_CUSTOM_EXTENSION) *stack = ssl->ctx->client_custom_extensions; + if (ssl->server) { + stack = ssl->ctx->server_custom_extensions; + } + + if (stack == NULL) { + return 1; + } + + for (size_t i = 0; i < sk_SSL_CUSTOM_EXTENSION_num(stack); i++) { + const SSL_CUSTOM_EXTENSION *ext = sk_SSL_CUSTOM_EXTENSION_value(stack, i); + + if (ssl->server && + !(hs->custom_extensions.received & (1u << i))) { + /* Servers cannot echo extensions that the client didn't send. */ + continue; + } + + const uint8_t *contents; + size_t contents_len; + int alert = SSL_AD_DECODE_ERROR; + CBB contents_cbb; + + switch (ext->add_callback(ssl, ext->value, &contents, &contents_len, &alert, + ext->add_arg)) { + case 1: + if (!CBB_add_u16(extensions, ext->value) || + !CBB_add_u16_length_prefixed(extensions, &contents_cbb) || + !CBB_add_bytes(&contents_cbb, contents, contents_len) || + !CBB_flush(extensions)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + ERR_add_error_dataf("extension %u", (unsigned) ext->value); + if (ext->free_callback && 0 < contents_len) { + ext->free_callback(ssl, ext->value, contents, ext->add_arg); + } + return 0; + } + + if (ext->free_callback && 0 < contents_len) { + ext->free_callback(ssl, ext->value, contents, ext->add_arg); + } + + if (!ssl->server) { + assert((hs->custom_extensions.sent & (1u << i)) == 0); + hs->custom_extensions.sent |= (1u << i); + } + break; + + case 0: + break; + + default: + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR); + ERR_add_error_dataf("extension %u", (unsigned) ext->value); + return 0; + } + } + + return 1; +} + +int custom_ext_add_clienthello(SSL_HANDSHAKE *hs, CBB *extensions) { + return custom_ext_add_hello(hs, extensions); +} + +int custom_ext_parse_serverhello(SSL_HANDSHAKE *hs, int *out_alert, + uint16_t value, const CBS *extension) { + SSL *const ssl = hs->ssl; + unsigned index; + const SSL_CUSTOM_EXTENSION *ext = + custom_ext_find(ssl->ctx->client_custom_extensions, &index, value); + + if (/* Unknown extensions are not allowed in a ServerHello. */ + ext == NULL || + /* Also, if we didn't send the extension, that's also unacceptable. */ + !(hs->custom_extensions.sent & (1u << index))) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + ERR_add_error_dataf("extension %u", (unsigned)value); + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + if (ext->parse_callback != NULL && + !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension), + out_alert, ext->parse_arg)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR); + ERR_add_error_dataf("extension %u", (unsigned)ext->value); + return 0; + } + + return 1; +} + +int custom_ext_parse_clienthello(SSL_HANDSHAKE *hs, int *out_alert, + uint16_t value, const CBS *extension) { + SSL *const ssl = hs->ssl; + unsigned index; + const SSL_CUSTOM_EXTENSION *ext = + custom_ext_find(ssl->ctx->server_custom_extensions, &index, value); + + if (ext == NULL) { + return 1; + } + + assert((hs->custom_extensions.received & (1u << index)) == 0); + hs->custom_extensions.received |= (1u << index); + + if (ext->parse_callback && + !ext->parse_callback(ssl, value, CBS_data(extension), CBS_len(extension), + out_alert, ext->parse_arg)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CUSTOM_EXTENSION_ERROR); + ERR_add_error_dataf("extension %u", (unsigned)ext->value); + return 0; + } + + return 1; +} + +int custom_ext_add_serverhello(SSL_HANDSHAKE *hs, CBB *extensions) { + return custom_ext_add_hello(hs, extensions); +} + +/* MAX_NUM_CUSTOM_EXTENSIONS is the maximum number of custom extensions that + * can be set on an |SSL_CTX|. It's determined by the size of the bitset used + * to track when an extension has been sent. */ +#define MAX_NUM_CUSTOM_EXTENSIONS \ + (sizeof(((SSL_HANDSHAKE *)NULL)->custom_extensions.sent) * 8) + +static int custom_ext_append(STACK_OF(SSL_CUSTOM_EXTENSION) **stack, + unsigned extension_value, + SSL_custom_ext_add_cb add_cb, + SSL_custom_ext_free_cb free_cb, void *add_arg, + SSL_custom_ext_parse_cb parse_cb, + void *parse_arg) { + if (add_cb == NULL || + 0xffff < extension_value || + SSL_extension_supported(extension_value) || + /* Specifying a free callback without an add callback is nonsensical + * and an error. */ + (*stack != NULL && + (MAX_NUM_CUSTOM_EXTENSIONS <= sk_SSL_CUSTOM_EXTENSION_num(*stack) || + custom_ext_find(*stack, NULL, extension_value) != NULL))) { + return 0; + } + + SSL_CUSTOM_EXTENSION *ext = OPENSSL_malloc(sizeof(SSL_CUSTOM_EXTENSION)); + if (ext == NULL) { + return 0; + } + ext->add_callback = add_cb; + ext->add_arg = add_arg; + ext->free_callback = free_cb; + ext->parse_callback = parse_cb; + ext->parse_arg = parse_arg; + ext->value = extension_value; + + if (*stack == NULL) { + *stack = sk_SSL_CUSTOM_EXTENSION_new_null(); + if (*stack == NULL) { + SSL_CUSTOM_EXTENSION_free(ext); + return 0; + } + } + + if (!sk_SSL_CUSTOM_EXTENSION_push(*stack, ext)) { + SSL_CUSTOM_EXTENSION_free(ext); + if (sk_SSL_CUSTOM_EXTENSION_num(*stack) == 0) { + sk_SSL_CUSTOM_EXTENSION_free(*stack); + *stack = NULL; + } + return 0; + } + + return 1; +} + +int SSL_CTX_add_client_custom_ext(SSL_CTX *ctx, unsigned extension_value, + SSL_custom_ext_add_cb add_cb, + SSL_custom_ext_free_cb free_cb, void *add_arg, + SSL_custom_ext_parse_cb parse_cb, + void *parse_arg) { + return custom_ext_append(&ctx->client_custom_extensions, extension_value, + add_cb ? add_cb : default_add_callback, free_cb, + add_arg, parse_cb, parse_arg); +} + +int SSL_CTX_add_server_custom_ext(SSL_CTX *ctx, unsigned extension_value, + SSL_custom_ext_add_cb add_cb, + SSL_custom_ext_free_cb free_cb, void *add_arg, + SSL_custom_ext_parse_cb parse_cb, + void *parse_arg) { + return custom_ext_append(&ctx->server_custom_extensions, extension_value, + add_cb ? add_cb : default_add_callback, free_cb, + add_arg, parse_cb, parse_arg); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_both.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_both.c new file mode 100644 index 000000000..44e3f2ef6 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_both.c @@ -0,0 +1,814 @@ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/rand.h> +#include <openssl/type_check.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +/* TODO(davidben): 28 comes from the size of IP + UDP header. Is this reasonable + * for these values? Notably, why is kMinMTU a function of the transport + * protocol's overhead rather than, say, what's needed to hold a minimally-sized + * handshake fragment plus protocol overhead. */ + +/* kMinMTU is the minimum acceptable MTU value. */ +static const unsigned int kMinMTU = 256 - 28; + +/* kDefaultMTU is the default MTU value to use if neither the user nor + * the underlying BIO supplies one. */ +static const unsigned int kDefaultMTU = 1500 - 28; + + +/* Receiving handshake messages. */ + +static void dtls1_hm_fragment_free(hm_fragment *frag) { + if (frag == NULL) { + return; + } + OPENSSL_free(frag->data); + OPENSSL_free(frag->reassembly); + OPENSSL_free(frag); +} + +static hm_fragment *dtls1_hm_fragment_new(const struct hm_header_st *msg_hdr) { + hm_fragment *frag = OPENSSL_malloc(sizeof(hm_fragment)); + if (frag == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return NULL; + } + OPENSSL_memset(frag, 0, sizeof(hm_fragment)); + frag->type = msg_hdr->type; + frag->seq = msg_hdr->seq; + frag->msg_len = msg_hdr->msg_len; + + /* Allocate space for the reassembled message and fill in the header. */ + frag->data = OPENSSL_malloc(DTLS1_HM_HEADER_LENGTH + msg_hdr->msg_len); + if (frag->data == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + CBB cbb; + if (!CBB_init_fixed(&cbb, frag->data, DTLS1_HM_HEADER_LENGTH) || + !CBB_add_u8(&cbb, msg_hdr->type) || + !CBB_add_u24(&cbb, msg_hdr->msg_len) || + !CBB_add_u16(&cbb, msg_hdr->seq) || + !CBB_add_u24(&cbb, 0 /* frag_off */) || + !CBB_add_u24(&cbb, msg_hdr->msg_len) || + !CBB_finish(&cbb, NULL, NULL)) { + CBB_cleanup(&cbb); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* If the handshake message is empty, |frag->reassembly| is NULL. */ + if (msg_hdr->msg_len > 0) { + /* Initialize reassembly bitmask. */ + if (msg_hdr->msg_len + 7 < msg_hdr->msg_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + goto err; + } + size_t bitmask_len = (msg_hdr->msg_len + 7) / 8; + frag->reassembly = OPENSSL_malloc(bitmask_len); + if (frag->reassembly == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + OPENSSL_memset(frag->reassembly, 0, bitmask_len); + } + + return frag; + +err: + dtls1_hm_fragment_free(frag); + return NULL; +} + +/* bit_range returns a |uint8_t| with bits |start|, inclusive, to |end|, + * exclusive, set. */ +static uint8_t bit_range(size_t start, size_t end) { + return (uint8_t)(~((1u << start) - 1) & ((1u << end) - 1)); +} + +/* dtls1_hm_fragment_mark marks bytes |start|, inclusive, to |end|, exclusive, + * as received in |frag|. If |frag| becomes complete, it clears + * |frag->reassembly|. The range must be within the bounds of |frag|'s message + * and |frag->reassembly| must not be NULL. */ +static void dtls1_hm_fragment_mark(hm_fragment *frag, size_t start, + size_t end) { + size_t msg_len = frag->msg_len; + + if (frag->reassembly == NULL || start > end || end > msg_len) { + assert(0); + return; + } + /* A zero-length message will never have a pending reassembly. */ + assert(msg_len > 0); + + if ((start >> 3) == (end >> 3)) { + frag->reassembly[start >> 3] |= bit_range(start & 7, end & 7); + } else { + frag->reassembly[start >> 3] |= bit_range(start & 7, 8); + for (size_t i = (start >> 3) + 1; i < (end >> 3); i++) { + frag->reassembly[i] = 0xff; + } + if ((end & 7) != 0) { + frag->reassembly[end >> 3] |= bit_range(0, end & 7); + } + } + + /* Check if the fragment is complete. */ + for (size_t i = 0; i < (msg_len >> 3); i++) { + if (frag->reassembly[i] != 0xff) { + return; + } + } + if ((msg_len & 7) != 0 && + frag->reassembly[msg_len >> 3] != bit_range(0, msg_len & 7)) { + return; + } + + OPENSSL_free(frag->reassembly); + frag->reassembly = NULL; +} + +/* dtls1_is_current_message_complete returns one if the current handshake + * message is complete and zero otherwise. */ +static int dtls1_is_current_message_complete(const SSL *ssl) { + hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq % + SSL_MAX_HANDSHAKE_FLIGHT]; + return frag != NULL && frag->reassembly == NULL; +} + +/* dtls1_get_incoming_message returns the incoming message corresponding to + * |msg_hdr|. If none exists, it creates a new one and inserts it in the + * queue. Otherwise, it checks |msg_hdr| is consistent with the existing one. It + * returns NULL on failure. The caller does not take ownership of the result. */ +static hm_fragment *dtls1_get_incoming_message( + SSL *ssl, const struct hm_header_st *msg_hdr) { + if (msg_hdr->seq < ssl->d1->handshake_read_seq || + msg_hdr->seq - ssl->d1->handshake_read_seq >= SSL_MAX_HANDSHAKE_FLIGHT) { + return NULL; + } + + size_t idx = msg_hdr->seq % SSL_MAX_HANDSHAKE_FLIGHT; + hm_fragment *frag = ssl->d1->incoming_messages[idx]; + if (frag != NULL) { + assert(frag->seq == msg_hdr->seq); + /* The new fragment must be compatible with the previous fragments from this + * message. */ + if (frag->type != msg_hdr->type || + frag->msg_len != msg_hdr->msg_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_FRAGMENT_MISMATCH); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return NULL; + } + return frag; + } + + /* This is the first fragment from this message. */ + frag = dtls1_hm_fragment_new(msg_hdr); + if (frag == NULL) { + return NULL; + } + ssl->d1->incoming_messages[idx] = frag; + return frag; +} + +/* dtls1_process_handshake_record reads a handshake record and processes it. It + * returns one if the record was successfully processed and 0 or -1 on error. */ +static int dtls1_process_handshake_record(SSL *ssl) { + SSL3_RECORD *rr = &ssl->s3->rrec; + +start: + if (rr->length == 0) { + int ret = dtls1_get_record(ssl); + if (ret <= 0) { + return ret; + } + } + + /* Cross-epoch records are discarded, but we may receive out-of-order + * application data between ChangeCipherSpec and Finished or a + * ChangeCipherSpec before the appropriate point in the handshake. Those must + * be silently discarded. + * + * However, only allow the out-of-order records in the correct epoch. + * Application data must come in the encrypted epoch, and ChangeCipherSpec in + * the unencrypted epoch (we never renegotiate). Other cases fall through and + * fail with a fatal error. */ + if ((rr->type == SSL3_RT_APPLICATION_DATA && + ssl->s3->aead_read_ctx != NULL) || + (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC && + ssl->s3->aead_read_ctx == NULL)) { + rr->length = 0; + goto start; + } + + if (rr->type != SSL3_RT_HANDSHAKE) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + return -1; + } + + CBS cbs; + CBS_init(&cbs, rr->data, rr->length); + + while (CBS_len(&cbs) > 0) { + /* Read a handshake fragment. */ + struct hm_header_st msg_hdr; + CBS body; + if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return -1; + } + + const size_t frag_off = msg_hdr.frag_off; + const size_t frag_len = msg_hdr.frag_len; + const size_t msg_len = msg_hdr.msg_len; + if (frag_off > msg_len || frag_off + frag_len < frag_off || + frag_off + frag_len > msg_len || + msg_len > ssl_max_handshake_message_len(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; + } + + /* The encrypted epoch in DTLS has only one handshake message. */ + if (ssl->d1->r_epoch == 1 && msg_hdr.seq != ssl->d1->handshake_read_seq) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return -1; + } + + if (msg_hdr.seq < ssl->d1->handshake_read_seq || + msg_hdr.seq > + (unsigned)ssl->d1->handshake_read_seq + SSL_MAX_HANDSHAKE_FLIGHT) { + /* Ignore fragments from the past, or ones too far in the future. */ + continue; + } + + hm_fragment *frag = dtls1_get_incoming_message(ssl, &msg_hdr); + if (frag == NULL) { + return -1; + } + assert(frag->msg_len == msg_len); + + if (frag->reassembly == NULL) { + /* The message is already assembled. */ + continue; + } + assert(msg_len > 0); + + /* Copy the body into the fragment. */ + OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off, + CBS_data(&body), CBS_len(&body)); + dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len); + } + + rr->length = 0; + ssl_read_buffer_discard(ssl); + return 1; +} + +int dtls1_get_message(SSL *ssl) { + if (ssl->s3->tmp.reuse_message) { + /* There must be a current message. */ + assert(ssl->init_msg != NULL); + ssl->s3->tmp.reuse_message = 0; + } else { + dtls1_release_current_message(ssl, 0 /* don't free buffer */); + } + + /* Process handshake records until the current message is ready. */ + while (!dtls1_is_current_message_complete(ssl)) { + int ret = dtls1_process_handshake_record(ssl); + if (ret <= 0) { + return ret; + } + } + + hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq % + SSL_MAX_HANDSHAKE_FLIGHT]; + assert(frag != NULL); + assert(frag->reassembly == NULL); + assert(ssl->d1->handshake_read_seq == frag->seq); + + /* TODO(davidben): This function has a lot of implicit outputs. Simplify the + * |ssl_get_message| API. */ + ssl->s3->tmp.message_type = frag->type; + ssl->init_msg = frag->data + DTLS1_HM_HEADER_LENGTH; + ssl->init_num = frag->msg_len; + + ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE, frag->data, + ssl->init_num + DTLS1_HM_HEADER_LENGTH); + return 1; +} + +void dtls1_get_current_message(const SSL *ssl, CBS *out) { + assert(dtls1_is_current_message_complete(ssl)); + + hm_fragment *frag = ssl->d1->incoming_messages[ssl->d1->handshake_read_seq % + SSL_MAX_HANDSHAKE_FLIGHT]; + CBS_init(out, frag->data, DTLS1_HM_HEADER_LENGTH + frag->msg_len); +} + +void dtls1_release_current_message(SSL *ssl, int free_buffer) { + if (ssl->init_msg == NULL) { + return; + } + + assert(dtls1_is_current_message_complete(ssl)); + size_t index = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT; + dtls1_hm_fragment_free(ssl->d1->incoming_messages[index]); + ssl->d1->incoming_messages[index] = NULL; + ssl->d1->handshake_read_seq++; + + ssl->init_msg = NULL; + ssl->init_num = 0; +} + +void dtls_clear_incoming_messages(SSL *ssl) { + for (size_t i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) { + dtls1_hm_fragment_free(ssl->d1->incoming_messages[i]); + ssl->d1->incoming_messages[i] = NULL; + } +} + +int dtls_has_incoming_messages(const SSL *ssl) { + size_t current = ssl->d1->handshake_read_seq % SSL_MAX_HANDSHAKE_FLIGHT; + for (size_t i = 0; i < SSL_MAX_HANDSHAKE_FLIGHT; i++) { + /* Skip the current message. */ + if (ssl->init_msg != NULL && i == current) { + assert(dtls1_is_current_message_complete(ssl)); + continue; + } + if (ssl->d1->incoming_messages[i] != NULL) { + return 1; + } + } + return 0; +} + +int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, + CBS *out_body) { + OPENSSL_memset(out_hdr, 0x00, sizeof(struct hm_header_st)); + + if (!CBS_get_u8(cbs, &out_hdr->type) || + !CBS_get_u24(cbs, &out_hdr->msg_len) || + !CBS_get_u16(cbs, &out_hdr->seq) || + !CBS_get_u24(cbs, &out_hdr->frag_off) || + !CBS_get_u24(cbs, &out_hdr->frag_len) || + !CBS_get_bytes(cbs, out_body, out_hdr->frag_len)) { + return 0; + } + + return 1; +} + + +/* Sending handshake messages. */ + +void dtls_clear_outgoing_messages(SSL *ssl) { + for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) { + OPENSSL_free(ssl->d1->outgoing_messages[i].data); + ssl->d1->outgoing_messages[i].data = NULL; + } + ssl->d1->outgoing_messages_len = 0; + ssl->d1->outgoing_written = 0; + ssl->d1->outgoing_offset = 0; +} + +int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { + /* Pick a modest size hint to save most of the |realloc| calls. */ + if (!CBB_init(cbb, 64) || + !CBB_add_u8(cbb, type) || + !CBB_add_u24(cbb, 0 /* length (filled in later) */) || + !CBB_add_u16(cbb, ssl->d1->handshake_write_seq) || + !CBB_add_u24(cbb, 0 /* offset */) || + !CBB_add_u24_length_prefixed(cbb, body)) { + return 0; + } + + return 1; +} + +int dtls1_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg, + size_t *out_len) { + *out_msg = NULL; + if (!CBB_finish(cbb, out_msg, out_len) || + *out_len < DTLS1_HM_HEADER_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + OPENSSL_free(*out_msg); + return 0; + } + + /* Fix up the header. Copy the fragment length into the total message + * length. */ + OPENSSL_memcpy(*out_msg + 1, *out_msg + DTLS1_HM_HEADER_LENGTH - 3, 3); + return 1; +} + +/* add_outgoing adds a new handshake message or ChangeCipherSpec to the current + * outgoing flight. It returns one on success and zero on error. In both cases, + * it takes ownership of |data| and releases it with |OPENSSL_free| when + * done. */ +static int add_outgoing(SSL *ssl, int is_ccs, uint8_t *data, size_t len) { + OPENSSL_COMPILE_ASSERT(SSL_MAX_HANDSHAKE_FLIGHT < + (1 << 8 * sizeof(ssl->d1->outgoing_messages_len)), + outgoing_messages_len_is_too_small); + if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) { + assert(0); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + OPENSSL_free(data); + return 0; + } + + if (!is_ccs) { + /* TODO(svaldez): Move this up a layer to fix abstraction for SSL_TRANSCRIPT + * on hs. */ + if (ssl->s3->hs != NULL && + !SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript, data, len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + OPENSSL_free(data); + return 0; + } + ssl->d1->handshake_write_seq++; + } + + DTLS_OUTGOING_MESSAGE *msg = + &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len]; + msg->data = data; + msg->len = len; + msg->epoch = ssl->d1->w_epoch; + msg->is_ccs = is_ccs; + + ssl->d1->outgoing_messages_len++; + return 1; +} + +int dtls1_add_message(SSL *ssl, uint8_t *data, size_t len) { + return add_outgoing(ssl, 0 /* handshake */, data, len); +} + +int dtls1_add_change_cipher_spec(SSL *ssl) { + return add_outgoing(ssl, 1 /* ChangeCipherSpec */, NULL, 0); +} + +int dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc) { + /* The |add_alert| path is only used for warning alerts for now, which DTLS + * never sends. This will be implemented later once closure alerts are + * converted. */ + assert(0); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; +} + +/* dtls1_update_mtu updates the current MTU from the BIO, ensuring it is above + * the minimum. */ +static void dtls1_update_mtu(SSL *ssl) { + /* TODO(davidben): No consumer implements |BIO_CTRL_DGRAM_SET_MTU| and the + * only |BIO_CTRL_DGRAM_QUERY_MTU| implementation could use + * |SSL_set_mtu|. Does this need to be so complex? */ + if (ssl->d1->mtu < dtls1_min_mtu() && + !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) { + long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); + if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) { + ssl->d1->mtu = (unsigned)mtu; + } else { + ssl->d1->mtu = kDefaultMTU; + BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL); + } + } + + /* The MTU should be above the minimum now. */ + assert(ssl->d1->mtu >= dtls1_min_mtu()); +} + +enum seal_result_t { + seal_error, + seal_no_progress, + seal_partial, + seal_success, +}; + +/* seal_next_message seals |msg|, which must be the next message, to |out|. If + * progress was made, it returns |seal_partial| or |seal_success| and sets + * |*out_len| to the number of bytes written. */ +static enum seal_result_t seal_next_message(SSL *ssl, uint8_t *out, + size_t *out_len, size_t max_out, + const DTLS_OUTGOING_MESSAGE *msg) { + assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len); + assert(msg == &ssl->d1->outgoing_messages[ssl->d1->outgoing_written]); + + /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 + * (negotiated cipher) exist. */ + assert(ssl->d1->w_epoch == 0 || ssl->d1->w_epoch == 1); + assert(msg->epoch <= ssl->d1->w_epoch); + enum dtls1_use_epoch_t use_epoch = dtls1_use_current_epoch; + if (ssl->d1->w_epoch == 1 && msg->epoch == 0) { + use_epoch = dtls1_use_previous_epoch; + } + size_t overhead = dtls_max_seal_overhead(ssl, use_epoch); + size_t prefix = dtls_seal_prefix_len(ssl, use_epoch); + + if (msg->is_ccs) { + /* Check there is room for the ChangeCipherSpec. */ + static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS}; + if (max_out < sizeof(kChangeCipherSpec) + overhead) { + return seal_no_progress; + } + + if (!dtls_seal_record(ssl, out, out_len, max_out, + SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec, + sizeof(kChangeCipherSpec), use_epoch)) { + return seal_error; + } + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC, + kChangeCipherSpec, sizeof(kChangeCipherSpec)); + return seal_success; + } + + /* DTLS messages are serialized as a single fragment in |msg|. */ + CBS cbs, body; + struct hm_header_st hdr; + CBS_init(&cbs, msg->data, msg->len); + if (!dtls1_parse_fragment(&cbs, &hdr, &body) || + hdr.frag_off != 0 || + hdr.frag_len != CBS_len(&body) || + hdr.msg_len != CBS_len(&body) || + !CBS_skip(&body, ssl->d1->outgoing_offset) || + CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return seal_error; + } + + /* Determine how much progress can be made. */ + if (max_out < DTLS1_HM_HEADER_LENGTH + 1 + overhead || max_out < prefix) { + return seal_no_progress; + } + size_t todo = CBS_len(&body); + if (todo > max_out - DTLS1_HM_HEADER_LENGTH - overhead) { + todo = max_out - DTLS1_HM_HEADER_LENGTH - overhead; + } + + /* Assemble a fragment, to be sealed in-place. */ + CBB cbb; + uint8_t *frag = out + prefix; + size_t max_frag = max_out - prefix, frag_len; + if (!CBB_init_fixed(&cbb, frag, max_frag) || + !CBB_add_u8(&cbb, hdr.type) || + !CBB_add_u24(&cbb, hdr.msg_len) || + !CBB_add_u16(&cbb, hdr.seq) || + !CBB_add_u24(&cbb, ssl->d1->outgoing_offset) || + !CBB_add_u24(&cbb, todo) || + !CBB_add_bytes(&cbb, CBS_data(&body), todo) || + !CBB_finish(&cbb, NULL, &frag_len)) { + CBB_cleanup(&cbb); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return seal_error; + } + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, frag, frag_len); + + if (!dtls_seal_record(ssl, out, out_len, max_out, SSL3_RT_HANDSHAKE, + out + prefix, frag_len, use_epoch)) { + return seal_error; + } + + if (todo == CBS_len(&body)) { + /* The next message is complete. */ + ssl->d1->outgoing_offset = 0; + return seal_success; + } + + ssl->d1->outgoing_offset += todo; + return seal_partial; +} + +/* seal_next_packet writes as much of the next flight as possible to |out| and + * advances |ssl->d1->outgoing_written| and |ssl->d1->outgoing_offset| as + * appropriate. */ +static int seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out) { + int made_progress = 0; + size_t total = 0; + assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len); + for (; ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len; + ssl->d1->outgoing_written++) { + const DTLS_OUTGOING_MESSAGE *msg = + &ssl->d1->outgoing_messages[ssl->d1->outgoing_written]; + size_t len; + enum seal_result_t ret = seal_next_message(ssl, out, &len, max_out, msg); + switch (ret) { + case seal_error: + return 0; + + case seal_no_progress: + goto packet_full; + + case seal_partial: + case seal_success: + out += len; + max_out -= len; + total += len; + made_progress = 1; + + if (ret == seal_partial) { + goto packet_full; + } + break; + } + } + +packet_full: + /* The MTU was too small to make any progress. */ + if (!made_progress) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL); + return 0; + } + + *out_len = total; + return 1; +} + +int dtls1_flush_flight(SSL *ssl) { + dtls1_update_mtu(ssl); + + int ret = -1; + uint8_t *packet = OPENSSL_malloc(ssl->d1->mtu); + if (packet == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + while (ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len) { + uint8_t old_written = ssl->d1->outgoing_written; + uint32_t old_offset = ssl->d1->outgoing_offset; + + size_t packet_len; + if (!seal_next_packet(ssl, packet, &packet_len, ssl->d1->mtu)) { + goto err; + } + + int bio_ret = BIO_write(ssl->wbio, packet, packet_len); + if (bio_ret <= 0) { + /* Retry this packet the next time around. */ + ssl->d1->outgoing_written = old_written; + ssl->d1->outgoing_offset = old_offset; + ssl->rwstate = SSL_WRITING; + ret = bio_ret; + goto err; + } + } + + if (BIO_flush(ssl->wbio) <= 0) { + ssl->rwstate = SSL_WRITING; + goto err; + } + + ret = 1; + +err: + OPENSSL_free(packet); + return ret; +} + +int dtls1_retransmit_outgoing_messages(SSL *ssl) { + /* Rewind to the start of the flight and write it again. + * + * TODO(davidben): This does not allow retransmits to be resumed on + * non-blocking write. */ + ssl->d1->outgoing_written = 0; + ssl->d1->outgoing_offset = 0; + + return dtls1_flush_flight(ssl); +} + +unsigned int dtls1_min_mtu(void) { + return kMinMTU; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_lib.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_lib.c new file mode 100644 index 000000000..ef15252fd --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_lib.c @@ -0,0 +1,266 @@ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/nid.h> + +#include "../crypto/internal.h" +#include "internal.h" + + + +/* DTLS1_MTU_TIMEOUTS is the maximum number of timeouts to expire + * before starting to decrease the MTU. */ +#define DTLS1_MTU_TIMEOUTS 2 + +/* DTLS1_MAX_TIMEOUTS is the maximum number of timeouts to expire + * before failing the DTLS handshake. */ +#define DTLS1_MAX_TIMEOUTS 12 + +int dtls1_new(SSL *ssl) { + DTLS1_STATE *d1; + + if (!ssl3_new(ssl)) { + return 0; + } + d1 = OPENSSL_malloc(sizeof *d1); + if (d1 == NULL) { + ssl3_free(ssl); + return 0; + } + OPENSSL_memset(d1, 0, sizeof *d1); + + ssl->d1 = d1; + + /* Set the version to the highest supported version. + * + * TODO(davidben): Move this field into |s3|, have it store the normalized + * protocol version, and implement this pre-negotiation quirk in |SSL_version| + * at the API boundary rather than in internal state. */ + ssl->version = DTLS1_2_VERSION; + return 1; +} + +void dtls1_free(SSL *ssl) { + ssl3_free(ssl); + + if (ssl == NULL || ssl->d1 == NULL) { + return; + } + + dtls_clear_incoming_messages(ssl); + dtls_clear_outgoing_messages(ssl); + + OPENSSL_free(ssl->d1); + ssl->d1 = NULL; +} + +void DTLSv1_set_initial_timeout_duration(SSL *ssl, unsigned int duration_ms) { + ssl->initial_timeout_duration_ms = duration_ms; +} + +void dtls1_start_timer(SSL *ssl) { + /* If timer is not set, initialize duration (by default, 1 second) */ + if (ssl->d1->next_timeout.tv_sec == 0 && ssl->d1->next_timeout.tv_usec == 0) { + ssl->d1->timeout_duration_ms = ssl->initial_timeout_duration_ms; + } + + /* Set timeout to current time */ + ssl_get_current_time(ssl, &ssl->d1->next_timeout); + + /* Add duration to current time */ + ssl->d1->next_timeout.tv_sec += ssl->d1->timeout_duration_ms / 1000; + ssl->d1->next_timeout.tv_usec += (ssl->d1->timeout_duration_ms % 1000) * 1000; + if (ssl->d1->next_timeout.tv_usec >= 1000000) { + ssl->d1->next_timeout.tv_sec++; + ssl->d1->next_timeout.tv_usec -= 1000000; + } +} + +int DTLSv1_get_timeout(const SSL *ssl, struct timeval *out) { + if (!SSL_is_dtls(ssl)) { + return 0; + } + + /* If no timeout is set, just return NULL */ + if (ssl->d1->next_timeout.tv_sec == 0 && ssl->d1->next_timeout.tv_usec == 0) { + return 0; + } + + struct OPENSSL_timeval timenow; + ssl_get_current_time(ssl, &timenow); + + /* If timer already expired, set remaining time to 0 */ + if (ssl->d1->next_timeout.tv_sec < timenow.tv_sec || + (ssl->d1->next_timeout.tv_sec == timenow.tv_sec && + ssl->d1->next_timeout.tv_usec <= timenow.tv_usec)) { + OPENSSL_memset(out, 0, sizeof(*out)); + return 1; + } + + /* Calculate time left until timer expires */ + struct OPENSSL_timeval ret; + OPENSSL_memcpy(&ret, &ssl->d1->next_timeout, sizeof(ret)); + ret.tv_sec -= timenow.tv_sec; + if (ret.tv_usec >= timenow.tv_usec) { + ret.tv_usec -= timenow.tv_usec; + } else { + ret.tv_usec = 1000000 + ret.tv_usec - timenow.tv_usec; + ret.tv_sec--; + } + + /* If remaining time is less than 15 ms, set it to 0 to prevent issues + * because of small divergences with socket timeouts. */ + if (ret.tv_sec == 0 && ret.tv_usec < 15000) { + OPENSSL_memset(&ret, 0, sizeof(ret)); + } + + /* Clamp the result in case of overflow. */ + if (ret.tv_sec > INT_MAX) { + assert(0); + out->tv_sec = INT_MAX; + } else { + out->tv_sec = ret.tv_sec; + } + + out->tv_usec = ret.tv_usec; + return 1; +} + +int dtls1_is_timer_expired(SSL *ssl) { + struct timeval timeleft; + + /* Get time left until timeout, return false if no timer running */ + if (!DTLSv1_get_timeout(ssl, &timeleft)) { + return 0; + } + + /* Return false if timer is not expired yet */ + if (timeleft.tv_sec > 0 || timeleft.tv_usec > 0) { + return 0; + } + + /* Timer expired, so return true */ + return 1; +} + +void dtls1_double_timeout(SSL *ssl) { + ssl->d1->timeout_duration_ms *= 2; + if (ssl->d1->timeout_duration_ms > 60000) { + ssl->d1->timeout_duration_ms = 60000; + } + dtls1_start_timer(ssl); +} + +void dtls1_stop_timer(SSL *ssl) { + /* Reset everything */ + ssl->d1->num_timeouts = 0; + OPENSSL_memset(&ssl->d1->next_timeout, 0, sizeof(ssl->d1->next_timeout)); + ssl->d1->timeout_duration_ms = ssl->initial_timeout_duration_ms; + + /* Clear retransmission buffer */ + dtls_clear_outgoing_messages(ssl); +} + +int dtls1_check_timeout_num(SSL *ssl) { + ssl->d1->num_timeouts++; + + /* Reduce MTU after 2 unsuccessful retransmissions */ + if (ssl->d1->num_timeouts > DTLS1_MTU_TIMEOUTS && + !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) { + long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL); + if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) { + ssl->d1->mtu = (unsigned)mtu; + } + } + + if (ssl->d1->num_timeouts > DTLS1_MAX_TIMEOUTS) { + /* fail the connection, enough alerts have been sent */ + OPENSSL_PUT_ERROR(SSL, SSL_R_READ_TIMEOUT_EXPIRED); + return -1; + } + + return 0; +} + +int DTLSv1_handle_timeout(SSL *ssl) { + ssl_reset_error_state(ssl); + + if (!SSL_is_dtls(ssl)) { + return -1; + } + + /* if no timer is expired, don't do anything */ + if (!dtls1_is_timer_expired(ssl)) { + return 0; + } + + dtls1_double_timeout(ssl); + + if (dtls1_check_timeout_num(ssl) < 0) { + return -1; + } + + dtls1_start_timer(ssl); + return dtls1_retransmit_outgoing_messages(ssl); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_pkt.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_pkt.c new file mode 100644 index 000000000..34448259e --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_pkt.c @@ -0,0 +1,414 @@ +/* DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. */ +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bio.h> +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/mem.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/rand.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +int dtls1_get_record(SSL *ssl) { +again: + switch (ssl->s3->recv_shutdown) { + case ssl_shutdown_none: + break; + case ssl_shutdown_fatal_alert: + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + case ssl_shutdown_close_notify: + return 0; + } + + /* Read a new packet if there is no unconsumed one. */ + if (ssl_read_buffer_len(ssl) == 0) { + int read_ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */); + if (read_ret < 0 && dtls1_is_timer_expired(ssl)) { + /* Historically, timeouts were handled implicitly if the caller did not + * handle them. + * + * TODO(davidben): This was to support blocking sockets but affected + * non-blocking sockets. Can it be removed? */ + int timeout_ret = DTLSv1_handle_timeout(ssl); + if (timeout_ret <= 0) { + return timeout_ret; + } + goto again; + } + if (read_ret <= 0) { + return read_ret; + } + } + assert(ssl_read_buffer_len(ssl) > 0); + + CBS body; + uint8_t type, alert; + size_t consumed; + enum ssl_open_record_t open_ret = + dtls_open_record(ssl, &type, &body, &consumed, &alert, + ssl_read_buffer(ssl), ssl_read_buffer_len(ssl)); + ssl_read_buffer_consume(ssl, consumed); + switch (open_ret) { + case ssl_open_record_partial: + /* Impossible in DTLS. */ + break; + + case ssl_open_record_success: + if (CBS_len(&body) > 0xffff) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return -1; + } + + SSL3_RECORD *rr = &ssl->s3->rrec; + rr->type = type; + rr->length = (uint16_t)CBS_len(&body); + rr->data = (uint8_t *)CBS_data(&body); + return 1; + + case ssl_open_record_discard: + goto again; + + case ssl_open_record_close_notify: + return 0; + + case ssl_open_record_fatal_alert: + return -1; + + case ssl_open_record_error: + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; + } + + assert(0); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; +} + +int dtls1_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len, + int peek) { + assert(!SSL_in_init(ssl)); + + *out_got_handshake = 0; + SSL3_RECORD *rr = &ssl->s3->rrec; + +again: + if (rr->length == 0) { + int ret = dtls1_get_record(ssl); + if (ret <= 0) { + return ret; + } + } + + if (rr->type == SSL3_RT_HANDSHAKE) { + /* Parse the first fragment header to determine if this is a pre-CCS or + * post-CCS handshake record. DTLS resets handshake message numbers on each + * handshake, so renegotiations and retransmissions are ambiguous. */ + CBS cbs, body; + struct hm_header_st msg_hdr; + CBS_init(&cbs, rr->data, rr->length); + if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD); + return -1; + } + + if (msg_hdr.type == SSL3_MT_FINISHED && + msg_hdr.seq == ssl->d1->handshake_read_seq - 1) { + if (msg_hdr.frag_off == 0) { + /* Retransmit our last flight of messages. If the peer sends the second + * Finished, they may not have received ours. Only do this for the + * first fragment, in case the Finished was fragmented. */ + if (dtls1_check_timeout_num(ssl) < 0) { + return -1; + } + + dtls1_retransmit_outgoing_messages(ssl); + } + + rr->length = 0; + goto again; + } + + /* Otherwise, this is a pre-CCS handshake message from an unsupported + * renegotiation attempt. Fall through to the error path. */ + } + + if (rr->type != SSL3_RT_APPLICATION_DATA) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + return -1; + } + + /* Discard empty records. */ + if (rr->length == 0) { + goto again; + } + + if (len <= 0) { + return len; + } + + if ((unsigned)len > rr->length) { + len = rr->length; + } + + OPENSSL_memcpy(buf, rr->data, len); + if (!peek) { + /* TODO(davidben): Should the record be truncated instead? This is a + * datagram transport. See https://crbug.com/boringssl/65. */ + rr->length -= len; + rr->data += len; + if (rr->length == 0) { + /* The record has been consumed, so we may now clear the buffer. */ + ssl_read_buffer_discard(ssl); + } + } + + return len; +} + +int dtls1_read_change_cipher_spec(SSL *ssl) { + SSL3_RECORD *rr = &ssl->s3->rrec; + +again: + if (rr->length == 0) { + int ret = dtls1_get_record(ssl); + if (ret <= 0) { + return ret; + } + } + + /* Drop handshake records silently. The epochs match, so this must be a + * retransmit of a message we already received. */ + if (rr->type == SSL3_RT_HANDSHAKE) { + rr->length = 0; + goto again; + } + + /* Other record types are illegal in this epoch. Note all application data + * records come in the encrypted epoch. */ + if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + return -1; + } + + if (rr->length != 1 || rr->data[0] != SSL3_MT_CCS) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; + } + + ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, + rr->length); + + rr->length = 0; + ssl_read_buffer_discard(ssl); + return 1; +} + +void dtls1_read_close_notify(SSL *ssl) { + /* Bidirectional shutdown doesn't make sense for an unordered transport. DTLS + * alerts also aren't delivered reliably, so we may even time out because the + * peer never received our close_notify. Report to the caller that the channel + * has fully shut down. */ + if (ssl->s3->recv_shutdown == ssl_shutdown_none) { + ssl->s3->recv_shutdown = ssl_shutdown_close_notify; + } +} + +int dtls1_write_app_data(SSL *ssl, const uint8_t *buf, int len) { + assert(!SSL_in_init(ssl)); + + if (len > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DTLS_MESSAGE_TOO_BIG); + return -1; + } + + if (len < 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_LENGTH); + return -1; + } + + if (len == 0) { + return 0; + } + + int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf, (size_t)len, + dtls1_use_current_epoch); + if (ret <= 0) { + return ret; + } + return len; +} + +int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len, + enum dtls1_use_epoch_t use_epoch) { + assert(len <= SSL3_RT_MAX_PLAIN_LENGTH); + /* There should never be a pending write buffer in DTLS. One can't write half + * a datagram, so the write buffer is always dropped in + * |ssl_write_buffer_flush|. */ + assert(!ssl_write_buffer_is_pending(ssl)); + + if (len > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + size_t max_out = len + SSL_max_seal_overhead(ssl); + uint8_t *out; + size_t ciphertext_len; + if (!ssl_write_buffer_init(ssl, &out, max_out) || + !dtls_seal_record(ssl, out, &ciphertext_len, max_out, type, buf, len, + use_epoch)) { + ssl_write_buffer_clear(ssl); + return -1; + } + ssl_write_buffer_set_len(ssl, ciphertext_len); + + int ret = ssl_write_buffer_flush(ssl); + if (ret <= 0) { + return ret; + } + return 1; +} + +int dtls1_dispatch_alert(SSL *ssl) { + int ret = dtls1_write_record(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2, + dtls1_use_current_epoch); + if (ret <= 0) { + return ret; + } + ssl->s3->alert_dispatch = 0; + + /* If the alert is fatal, flush the BIO now. */ + if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) { + BIO_flush(ssl->wbio); + } + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert, + 2); + + int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1]; + ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, alert); + + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_srtp.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_srtp.c new file mode 100644 index 000000000..108537777 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/d1_srtp.c @@ -0,0 +1,236 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2006 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* + DTLS code by Eric Rescorla <ekr@rtfm.com> + + Copyright (C) 2006, Network Resonance, Inc. + Copyright (C) 2011, RTFM, Inc. +*/ + +#include <openssl/ssl.h> + +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> + +#include "internal.h" + + +static const SRTP_PROTECTION_PROFILE kSRTPProfiles[] = { + { + "SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80, + }, + { + "SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32, + }, + { + "SRTP_AEAD_AES_128_GCM", SRTP_AEAD_AES_128_GCM, + }, + { + "SRTP_AEAD_AES_256_GCM", SRTP_AEAD_AES_256_GCM, + }, + {0, 0}, +}; + +static int find_profile_by_name(const char *profile_name, + const SRTP_PROTECTION_PROFILE **pptr, + size_t len) { + const SRTP_PROTECTION_PROFILE *p; + + p = kSRTPProfiles; + while (p->name) { + if (len == strlen(p->name) && !strncmp(p->name, profile_name, len)) { + *pptr = p; + return 1; + } + + p++; + } + + return 0; +} + +static int ssl_ctx_make_profiles(const char *profiles_string, + STACK_OF(SRTP_PROTECTION_PROFILE) **out) { + STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = + sk_SRTP_PROTECTION_PROFILE_new_null(); + if (profiles == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES); + return 0; + } + + const char *col; + const char *ptr = profiles_string; + do { + col = strchr(ptr, ':'); + + const SRTP_PROTECTION_PROFILE *profile; + if (!find_profile_by_name(ptr, &profile, + col ? (size_t)(col - ptr) : strlen(ptr))) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE); + goto err; + } + + if (!sk_SRTP_PROTECTION_PROFILE_push(profiles, profile)) { + goto err; + } + + if (col) { + ptr = col + 1; + } + } while (col); + + sk_SRTP_PROTECTION_PROFILE_free(*out); + *out = profiles; + return 1; + +err: + sk_SRTP_PROTECTION_PROFILE_free(profiles); + return 0; +} + +int SSL_CTX_set_srtp_profiles(SSL_CTX *ctx, const char *profiles) { + return ssl_ctx_make_profiles(profiles, &ctx->srtp_profiles); +} + +int SSL_set_srtp_profiles(SSL *ssl, const char *profiles) { + return ssl_ctx_make_profiles(profiles, &ssl->srtp_profiles); +} + +STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(SSL *ssl) { + if (ssl == NULL) { + return NULL; + } + + if (ssl->srtp_profiles != NULL) { + return ssl->srtp_profiles; + } + + if (ssl->ctx->srtp_profiles != NULL) { + return ssl->ctx->srtp_profiles; + } + + return NULL; +} + +const SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *ssl) { + return ssl->srtp_profile; +} + +int SSL_CTX_set_tlsext_use_srtp(SSL_CTX *ctx, const char *profiles) { + /* This API inverts its return value. */ + return !SSL_CTX_set_srtp_profiles(ctx, profiles); +} + +int SSL_set_tlsext_use_srtp(SSL *ssl, const char *profiles) { + /* This API inverts its return value. */ + return !SSL_set_srtp_profiles(ssl, profiles); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/dtls_method.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/dtls_method.c new file mode 100644 index 000000000..60847895a --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/dtls_method.c @@ -0,0 +1,217 @@ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/err.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +static int dtls1_version_from_wire(uint16_t *out_version, + uint16_t wire_version) { + switch (wire_version) { + case DTLS1_VERSION: + /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */ + *out_version = TLS1_1_VERSION; + return 1; + case DTLS1_2_VERSION: + *out_version = TLS1_2_VERSION; + return 1; + } + + return 0; +} + +static uint16_t dtls1_version_to_wire(uint16_t version) { + switch (version) { + case TLS1_1_VERSION: + /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */ + return DTLS1_VERSION; + case TLS1_2_VERSION: + return DTLS1_2_VERSION; + } + + /* It is an error to use this function with an invalid version. */ + assert(0); + return 0; +} + +static int dtls1_supports_cipher(const SSL_CIPHER *cipher) { + return cipher->algorithm_enc != SSL_eNULL; +} + +static void dtls1_expect_flight(SSL *ssl) { dtls1_start_timer(ssl); } + +static void dtls1_received_flight(SSL *ssl) { dtls1_stop_timer(ssl); } + +static int dtls1_set_read_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) { + /* Cipher changes are illegal when there are buffered incoming messages. */ + if (dtls_has_incoming_messages(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + SSL_AEAD_CTX_free(aead_ctx); + return 0; + } + + ssl->d1->r_epoch++; + OPENSSL_memset(&ssl->d1->bitmap, 0, sizeof(ssl->d1->bitmap)); + OPENSSL_memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence)); + + SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx); + ssl->s3->aead_read_ctx = aead_ctx; + return 1; +} + +static int dtls1_set_write_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) { + ssl->d1->w_epoch++; + OPENSSL_memcpy(ssl->d1->last_write_sequence, ssl->s3->write_sequence, + sizeof(ssl->s3->write_sequence)); + OPENSSL_memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence)); + + SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx); + ssl->s3->aead_write_ctx = aead_ctx; + return 1; +} + +static const SSL_PROTOCOL_METHOD kDTLSProtocolMethod = { + 1 /* is_dtls */, + TLS1_1_VERSION, + TLS1_2_VERSION, + dtls1_version_from_wire, + dtls1_version_to_wire, + dtls1_new, + dtls1_free, + dtls1_get_message, + dtls1_get_current_message, + dtls1_release_current_message, + dtls1_read_app_data, + dtls1_read_change_cipher_spec, + dtls1_read_close_notify, + dtls1_write_app_data, + dtls1_dispatch_alert, + dtls1_supports_cipher, + dtls1_init_message, + dtls1_finish_message, + dtls1_add_message, + dtls1_add_change_cipher_spec, + dtls1_add_alert, + dtls1_flush_flight, + dtls1_expect_flight, + dtls1_received_flight, + dtls1_set_read_state, + dtls1_set_write_state, +}; + +const SSL_METHOD *DTLS_method(void) { + static const SSL_METHOD kMethod = { + 0, + &kDTLSProtocolMethod, + &ssl_crypto_x509_method, + }; + return &kMethod; +} + +/* Legacy version-locked methods. */ + +const SSL_METHOD *DTLSv1_2_method(void) { + static const SSL_METHOD kMethod = { + DTLS1_2_VERSION, + &kDTLSProtocolMethod, + &ssl_crypto_x509_method, + }; + return &kMethod; +} + +const SSL_METHOD *DTLSv1_method(void) { + static const SSL_METHOD kMethod = { + DTLS1_VERSION, + &kDTLSProtocolMethod, + &ssl_crypto_x509_method, + }; + return &kMethod; +} + +/* Legacy side-specific methods. */ + +const SSL_METHOD *DTLSv1_2_server_method(void) { + return DTLSv1_2_method(); +} + +const SSL_METHOD *DTLSv1_server_method(void) { + return DTLSv1_method(); +} + +const SSL_METHOD *DTLSv1_2_client_method(void) { + return DTLSv1_2_method(); +} + +const SSL_METHOD *DTLSv1_client_method(void) { + return DTLSv1_method(); +} + +const SSL_METHOD *DTLS_server_method(void) { + return DTLS_method(); +} + +const SSL_METHOD *DTLS_client_method(void) { + return DTLS_method(); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/dtls_record.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/dtls_record.c new file mode 100644 index 000000000..879706dfc --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/dtls_record.c @@ -0,0 +1,334 @@ +/* DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. */ +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> + +#include "internal.h" +#include "../crypto/internal.h" + + +/* to_u64_be treats |in| as a 8-byte big-endian integer and returns the value as + * a |uint64_t|. */ +static uint64_t to_u64_be(const uint8_t in[8]) { + uint64_t ret = 0; + unsigned i; + for (i = 0; i < 8; i++) { + ret <<= 8; + ret |= in[i]; + } + return ret; +} + +/* dtls1_bitmap_should_discard returns one if |seq_num| has been seen in |bitmap| + * or is stale. Otherwise it returns zero. */ +static int dtls1_bitmap_should_discard(DTLS1_BITMAP *bitmap, + const uint8_t seq_num[8]) { + const unsigned kWindowSize = sizeof(bitmap->map) * 8; + + uint64_t seq_num_u = to_u64_be(seq_num); + if (seq_num_u > bitmap->max_seq_num) { + return 0; + } + uint64_t idx = bitmap->max_seq_num - seq_num_u; + return idx >= kWindowSize || (bitmap->map & (((uint64_t)1) << idx)); +} + +/* dtls1_bitmap_record updates |bitmap| to record receipt of sequence number + * |seq_num|. It slides the window forward if needed. It is an error to call + * this function on a stale sequence number. */ +static void dtls1_bitmap_record(DTLS1_BITMAP *bitmap, + const uint8_t seq_num[8]) { + const unsigned kWindowSize = sizeof(bitmap->map) * 8; + + uint64_t seq_num_u = to_u64_be(seq_num); + /* Shift the window if necessary. */ + if (seq_num_u > bitmap->max_seq_num) { + uint64_t shift = seq_num_u - bitmap->max_seq_num; + if (shift >= kWindowSize) { + bitmap->map = 0; + } else { + bitmap->map <<= shift; + } + bitmap->max_seq_num = seq_num_u; + } + + uint64_t idx = bitmap->max_seq_num - seq_num_u; + if (idx < kWindowSize) { + bitmap->map |= ((uint64_t)1) << idx; + } +} + +enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type, CBS *out, + size_t *out_consumed, + uint8_t *out_alert, uint8_t *in, + size_t in_len) { + *out_consumed = 0; + + CBS cbs; + CBS_init(&cbs, in, in_len); + + /* Decode the record. */ + uint8_t type; + uint16_t version; + uint8_t sequence[8]; + CBS body; + if (!CBS_get_u8(&cbs, &type) || + !CBS_get_u16(&cbs, &version) || + !CBS_copy_bytes(&cbs, sequence, 8) || + !CBS_get_u16_length_prefixed(&cbs, &body) || + (ssl->s3->have_version && version != ssl->version) || + (version >> 8) != DTLS1_VERSION_MAJOR || + CBS_len(&body) > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + /* The record header was incomplete or malformed. Drop the entire packet. */ + *out_consumed = in_len; + return ssl_open_record_discard; + } + + ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HEADER, in, + DTLS1_RT_HEADER_LENGTH); + + uint16_t epoch = (((uint16_t)sequence[0]) << 8) | sequence[1]; + if (epoch != ssl->d1->r_epoch || + dtls1_bitmap_should_discard(&ssl->d1->bitmap, sequence)) { + /* Drop this record. It's from the wrong epoch or is a replay. Note that if + * |epoch| is the next epoch, the record could be buffered for later. For + * simplicity, drop it and expect retransmit to handle it later; DTLS must + * handle packet loss anyway. */ + *out_consumed = in_len - CBS_len(&cbs); + return ssl_open_record_discard; + } + + /* Decrypt the body in-place. */ + if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, type, version, sequence, + (uint8_t *)CBS_data(&body), CBS_len(&body))) { + /* Bad packets are silently dropped in DTLS. See section 4.2.1 of RFC 6347. + * Clear the error queue of any errors decryption may have added. Drop the + * entire packet as it must not have come from the peer. + * + * TODO(davidben): This doesn't distinguish malloc failures from encryption + * failures. */ + ERR_clear_error(); + *out_consumed = in_len - CBS_len(&cbs); + return ssl_open_record_discard; + } + *out_consumed = in_len - CBS_len(&cbs); + + /* Check the plaintext length. */ + if (CBS_len(out) > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + *out_alert = SSL_AD_RECORD_OVERFLOW; + return ssl_open_record_error; + } + + dtls1_bitmap_record(&ssl->d1->bitmap, sequence); + + /* TODO(davidben): Limit the number of empty records as in TLS? This is only + * useful if we also limit discarded packets. */ + + if (type == SSL3_RT_ALERT) { + return ssl_process_alert(ssl, out_alert, CBS_data(out), CBS_len(out)); + } + + ssl->s3->warning_alert_count = 0; + + *out_type = type; + return ssl_open_record_success; +} + +static const SSL_AEAD_CTX *get_write_aead(const SSL *ssl, + enum dtls1_use_epoch_t use_epoch) { + if (use_epoch == dtls1_use_previous_epoch) { + /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 + * (negotiated cipher) exist. */ + assert(ssl->d1->w_epoch == 1); + return NULL; + } + + return ssl->s3->aead_write_ctx; +} + +size_t dtls_max_seal_overhead(const SSL *ssl, + enum dtls1_use_epoch_t use_epoch) { + return DTLS1_RT_HEADER_LENGTH + + SSL_AEAD_CTX_max_overhead(get_write_aead(ssl, use_epoch)); +} + +size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) { + return DTLS1_RT_HEADER_LENGTH + + SSL_AEAD_CTX_explicit_nonce_len(get_write_aead(ssl, use_epoch)); +} + +int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint8_t type, const uint8_t *in, size_t in_len, + enum dtls1_use_epoch_t use_epoch) { + const size_t prefix = dtls_seal_prefix_len(ssl, use_epoch); + if (buffers_alias(in, in_len, out, max_out) && + (max_out < prefix || out + prefix != in)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); + return 0; + } + + /* Determine the parameters for the current epoch. */ + uint16_t epoch = ssl->d1->w_epoch; + SSL_AEAD_CTX *aead = ssl->s3->aead_write_ctx; + uint8_t *seq = ssl->s3->write_sequence; + if (use_epoch == dtls1_use_previous_epoch) { + /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1 + * (negotiated cipher) exist. */ + assert(ssl->d1->w_epoch == 1); + epoch = ssl->d1->w_epoch - 1; + aead = NULL; + seq = ssl->d1->last_write_sequence; + } + + if (max_out < DTLS1_RT_HEADER_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + + out[0] = type; + + uint16_t wire_version = ssl->s3->have_version ? ssl->version : DTLS1_VERSION; + out[1] = wire_version >> 8; + out[2] = wire_version & 0xff; + + out[3] = epoch >> 8; + out[4] = epoch & 0xff; + OPENSSL_memcpy(&out[5], &seq[2], 6); + + size_t ciphertext_len; + if (!SSL_AEAD_CTX_seal(aead, out + DTLS1_RT_HEADER_LENGTH, &ciphertext_len, + max_out - DTLS1_RT_HEADER_LENGTH, type, wire_version, + &out[3] /* seq */, in, in_len) || + !ssl_record_sequence_update(&seq[2], 6)) { + return 0; + } + + if (ciphertext_len >= 1 << 16) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + out[11] = ciphertext_len >> 8; + out[12] = ciphertext_len & 0xff; + + *out_len = DTLS1_RT_HEADER_LENGTH + ciphertext_len; + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, out, + DTLS1_RT_HEADER_LENGTH); + + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/handshake_client.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/handshake_client.c new file mode 100644 index 000000000..1feb7d8d0 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/handshake_client.c @@ -0,0 +1,1909 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * Portions of the attached software ("Contribution") are developed by + * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project. + * + * The Contribution is licensed pursuant to the OpenSSL open source + * license provided above. + * + * ECC cipher suite support in OpenSSL originally written by + * Vipul Gupta and Sumit Gupta of Sun Microsystems Laboratories. + * + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/aead.h> +#include <openssl/bn.h> +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/dh.h> +#include <openssl/ec_key.h> +#include <openssl/ecdsa.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/rand.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +static int ssl3_send_client_hello(SSL_HANDSHAKE *hs); +static int dtls1_get_hello_verify(SSL_HANDSHAKE *hs); +static int ssl3_get_server_hello(SSL_HANDSHAKE *hs); +static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs); +static int ssl3_get_cert_status(SSL_HANDSHAKE *hs); +static int ssl3_verify_server_cert(SSL_HANDSHAKE *hs); +static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs); +static int ssl3_get_certificate_request(SSL_HANDSHAKE *hs); +static int ssl3_get_server_hello_done(SSL_HANDSHAKE *hs); +static int ssl3_send_client_certificate(SSL_HANDSHAKE *hs); +static int ssl3_send_client_key_exchange(SSL_HANDSHAKE *hs); +static int ssl3_send_cert_verify(SSL_HANDSHAKE *hs); +static int ssl3_send_next_proto(SSL_HANDSHAKE *hs); +static int ssl3_send_channel_id(SSL_HANDSHAKE *hs); +static int ssl3_get_new_session_ticket(SSL_HANDSHAKE *hs); + +int ssl3_connect(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = -1; + + assert(ssl->handshake_func == ssl3_connect); + assert(!ssl->server); + + for (;;) { + int state = hs->state; + + switch (hs->state) { + case SSL_ST_INIT: + ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1); + hs->state = SSL3_ST_CW_CLNT_HELLO_A; + break; + + case SSL3_ST_CW_CLNT_HELLO_A: + ret = ssl3_send_client_hello(hs); + if (ret <= 0) { + goto end; + } + + if (!SSL_is_dtls(ssl) || ssl->d1->send_cookie) { + hs->next_state = SSL3_ST_CR_SRVR_HELLO_A; + } else { + hs->next_state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A; + } + hs->state = SSL3_ST_CW_FLUSH; + break; + + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: + assert(SSL_is_dtls(ssl)); + ret = dtls1_get_hello_verify(hs); + if (ret <= 0) { + goto end; + } + if (ssl->d1->send_cookie) { + ssl->method->received_flight(ssl); + hs->state = SSL3_ST_CW_CLNT_HELLO_A; + } else { + hs->state = SSL3_ST_CR_SRVR_HELLO_A; + } + break; + + case SSL3_ST_CR_SRVR_HELLO_A: + ret = ssl3_get_server_hello(hs); + if (hs->state == SSL_ST_TLS13) { + break; + } + if (ret <= 0) { + goto end; + } + + if (ssl->session != NULL) { + hs->state = SSL3_ST_CR_SESSION_TICKET_A; + } else { + hs->state = SSL3_ST_CR_CERT_A; + } + break; + + case SSL3_ST_CR_CERT_A: + if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + ret = ssl3_get_server_certificate(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_CR_CERT_STATUS_A; + break; + + case SSL3_ST_CR_CERT_STATUS_A: + if (hs->certificate_status_expected) { + ret = ssl3_get_cert_status(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_VERIFY_SERVER_CERT; + break; + + case SSL3_ST_VERIFY_SERVER_CERT: + if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + ret = ssl3_verify_server_cert(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_CR_KEY_EXCH_A; + break; + + case SSL3_ST_CR_KEY_EXCH_A: + ret = ssl3_get_server_key_exchange(hs); + if (ret <= 0) { + goto end; + } + hs->state = SSL3_ST_CR_CERT_REQ_A; + break; + + case SSL3_ST_CR_CERT_REQ_A: + if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + ret = ssl3_get_certificate_request(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_CR_SRVR_DONE_A; + break; + + case SSL3_ST_CR_SRVR_DONE_A: + ret = ssl3_get_server_hello_done(hs); + if (ret <= 0) { + goto end; + } + ssl->method->received_flight(ssl); + hs->state = SSL3_ST_CW_CERT_A; + break; + + case SSL3_ST_CW_CERT_A: + if (hs->cert_request) { + ret = ssl3_send_client_certificate(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_CW_KEY_EXCH_A; + break; + + case SSL3_ST_CW_KEY_EXCH_A: + ret = ssl3_send_client_key_exchange(hs); + if (ret <= 0) { + goto end; + } + hs->state = SSL3_ST_CW_CERT_VRFY_A; + break; + + case SSL3_ST_CW_CERT_VRFY_A: + case SSL3_ST_CW_CERT_VRFY_B: + if (hs->cert_request && ssl_has_certificate(ssl)) { + ret = ssl3_send_cert_verify(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_CW_CHANGE; + break; + + case SSL3_ST_CW_CHANGE: + if (!ssl->method->add_change_cipher_spec(ssl) || + !tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) { + ret = -1; + goto end; + } + + hs->state = SSL3_ST_CW_NEXT_PROTO_A; + break; + + case SSL3_ST_CW_NEXT_PROTO_A: + if (hs->next_proto_neg_seen) { + ret = ssl3_send_next_proto(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_CW_CHANNEL_ID_A; + break; + + case SSL3_ST_CW_CHANNEL_ID_A: + if (ssl->s3->tlsext_channel_id_valid) { + ret = ssl3_send_channel_id(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_CW_FINISHED_A; + break; + + case SSL3_ST_CW_FINISHED_A: + ret = ssl3_send_finished(hs); + if (ret <= 0) { + goto end; + } + hs->state = SSL3_ST_CW_FLUSH; + + if (ssl->session != NULL) { + hs->next_state = SSL3_ST_FINISH_CLIENT_HANDSHAKE; + } else { + /* This is a non-resumption handshake. If it involves ChannelID, then + * record the handshake hashes at this point in the session so that + * any resumption of this session with ChannelID can sign those + * hashes. */ + ret = tls1_record_handshake_hashes_for_channel_id(hs); + if (ret <= 0) { + goto end; + } + if ((SSL_get_mode(ssl) & SSL_MODE_ENABLE_FALSE_START) && + ssl3_can_false_start(ssl) && + /* No False Start on renegotiation (would complicate the state + * machine). */ + !ssl->s3->initial_handshake_complete) { + hs->next_state = SSL3_ST_FALSE_START; + } else { + hs->next_state = SSL3_ST_CR_SESSION_TICKET_A; + } + } + break; + + case SSL3_ST_FALSE_START: + hs->state = SSL3_ST_CR_SESSION_TICKET_A; + hs->in_false_start = 1; + ret = 1; + goto end; + + case SSL3_ST_CR_SESSION_TICKET_A: + if (hs->ticket_expected) { + ret = ssl3_get_new_session_ticket(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_CR_CHANGE; + break; + + case SSL3_ST_CR_CHANGE: + ret = ssl->method->read_change_cipher_spec(ssl); + if (ret <= 0) { + goto end; + } + + if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_CLIENT_READ)) { + ret = -1; + goto end; + } + hs->state = SSL3_ST_CR_FINISHED_A; + break; + + case SSL3_ST_CR_FINISHED_A: + ret = ssl3_get_finished(hs); + if (ret <= 0) { + goto end; + } + ssl->method->received_flight(ssl); + + if (ssl->session != NULL) { + hs->state = SSL3_ST_CW_CHANGE; + } else { + hs->state = SSL3_ST_FINISH_CLIENT_HANDSHAKE; + } + break; + + case SSL3_ST_CW_FLUSH: + ret = ssl->method->flush_flight(ssl); + if (ret <= 0) { + goto end; + } + hs->state = hs->next_state; + if (hs->state != SSL3_ST_FINISH_CLIENT_HANDSHAKE) { + ssl->method->expect_flight(ssl); + } + break; + + case SSL_ST_TLS13: + ret = tls13_handshake(hs); + if (ret <= 0) { + goto end; + } + hs->state = SSL3_ST_FINISH_CLIENT_HANDSHAKE; + break; + + case SSL3_ST_FINISH_CLIENT_HANDSHAKE: + ssl->method->release_current_message(ssl, 1 /* free_buffer */); + + SSL_SESSION_free(ssl->s3->established_session); + if (ssl->session != NULL) { + SSL_SESSION_up_ref(ssl->session); + ssl->s3->established_session = ssl->session; + } else { + /* We make a copy of the session in order to maintain the immutability + * of the new established_session due to False Start. The caller may + * have taken a reference to the temporary session. */ + ssl->s3->established_session = + SSL_SESSION_dup(hs->new_session, SSL_SESSION_DUP_ALL); + if (ssl->s3->established_session == NULL) { + ret = -1; + goto end; + } + ssl->s3->established_session->not_resumable = 0; + + SSL_SESSION_free(hs->new_session); + hs->new_session = NULL; + } + + hs->state = SSL_ST_OK; + break; + + case SSL_ST_OK: { + const int is_initial_handshake = !ssl->s3->initial_handshake_complete; + ssl->s3->initial_handshake_complete = 1; + if (is_initial_handshake) { + /* Renegotiations do not participate in session resumption. */ + ssl_update_cache(hs, SSL_SESS_CACHE_CLIENT); + } + + ret = 1; + ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1); + goto end; + } + + default: + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); + ret = -1; + goto end; + } + + if (hs->state != state) { + ssl_do_info_callback(ssl, SSL_CB_CONNECT_LOOP, 1); + } + } + +end: + ssl_do_info_callback(ssl, SSL_CB_CONNECT_EXIT, ret); + return ret; +} + +uint16_t ssl_get_grease_value(const SSL *ssl, enum ssl_grease_index_t index) { + /* Use the client_random for entropy. This both avoids calling |RAND_bytes| on + * a single byte repeatedly and ensures the values are deterministic. This + * allows the same ClientHello be sent twice for a HelloRetryRequest or the + * same group be advertised in both supported_groups and key_shares. */ + uint16_t ret = ssl->s3->client_random[index]; + /* This generates a random value of the form 0xωaωa, for all 0 ≤ ω < 16. */ + ret = (ret & 0xf0) | 0x0a; + ret |= ret << 8; + return ret; +} + +/* ssl_get_client_disabled sets |*out_mask_a| and |*out_mask_k| to masks of + * disabled algorithms. */ +static void ssl_get_client_disabled(SSL *ssl, uint32_t *out_mask_a, + uint32_t *out_mask_k) { + int have_rsa = 0, have_ecdsa = 0; + *out_mask_a = 0; + *out_mask_k = 0; + + /* Now go through all signature algorithms seeing if we support any for RSA or + * ECDSA. Do this for all versions not just TLS 1.2. */ + const uint16_t *sigalgs; + size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs); + for (size_t i = 0; i < num_sigalgs; i++) { + switch (sigalgs[i]) { + case SSL_SIGN_RSA_PSS_SHA512: + case SSL_SIGN_RSA_PSS_SHA384: + case SSL_SIGN_RSA_PSS_SHA256: + case SSL_SIGN_RSA_PKCS1_SHA512: + case SSL_SIGN_RSA_PKCS1_SHA384: + case SSL_SIGN_RSA_PKCS1_SHA256: + case SSL_SIGN_RSA_PKCS1_SHA1: + have_rsa = 1; + break; + + case SSL_SIGN_ECDSA_SECP521R1_SHA512: + case SSL_SIGN_ECDSA_SECP384R1_SHA384: + case SSL_SIGN_ECDSA_SECP256R1_SHA256: + case SSL_SIGN_ECDSA_SHA1: + have_ecdsa = 1; + break; + } + } + + /* Disable auth if we don't include any appropriate signature algorithms. */ + if (!have_rsa) { + *out_mask_a |= SSL_aRSA; + } + if (!have_ecdsa) { + *out_mask_a |= SSL_aECDSA; + } + + /* PSK requires a client callback. */ + if (ssl->psk_client_callback == NULL) { + *out_mask_a |= SSL_aPSK; + *out_mask_k |= SSL_kPSK; + } +} + +static int ssl_write_client_cipher_list(SSL *ssl, CBB *out, + uint16_t min_version, + uint16_t max_version) { + uint32_t mask_a, mask_k; + ssl_get_client_disabled(ssl, &mask_a, &mask_k); + + CBB child; + if (!CBB_add_u16_length_prefixed(out, &child)) { + return 0; + } + + /* Add a fake cipher suite. See draft-davidben-tls-grease-01. */ + if (ssl->ctx->grease_enabled && + !CBB_add_u16(&child, ssl_get_grease_value(ssl, ssl_grease_cipher))) { + return 0; + } + + /* Add TLS 1.3 ciphers. Order ChaCha20-Poly1305 relative to AES-GCM based on + * hardware support. */ + if (max_version >= TLS1_3_VERSION) { + if (!EVP_has_aes_hardware() && + !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) { + return 0; + } + if (!CBB_add_u16(&child, TLS1_CK_AES_128_GCM_SHA256 & 0xffff) || + !CBB_add_u16(&child, TLS1_CK_AES_256_GCM_SHA384 & 0xffff)) { + return 0; + } + if (EVP_has_aes_hardware() && + !CBB_add_u16(&child, TLS1_CK_CHACHA20_POLY1305_SHA256 & 0xffff)) { + return 0; + } + } + + if (min_version < TLS1_3_VERSION) { + STACK_OF(SSL_CIPHER) *ciphers = SSL_get_ciphers(ssl); + int any_enabled = 0; + for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i); + /* Skip disabled ciphers */ + if ((cipher->algorithm_mkey & mask_k) || + (cipher->algorithm_auth & mask_a)) { + continue; + } + if (SSL_CIPHER_get_min_version(cipher) > max_version || + SSL_CIPHER_get_max_version(cipher) < min_version) { + continue; + } + any_enabled = 1; + if (!CBB_add_u16(&child, ssl_cipher_get_value(cipher))) { + return 0; + } + } + + /* If all ciphers were disabled, return the error to the caller. */ + if (!any_enabled && max_version < TLS1_3_VERSION) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHERS_AVAILABLE); + return 0; + } + } + + /* For SSLv3, the SCSV is added. Otherwise the renegotiation extension is + * added. */ + if (max_version == SSL3_VERSION && + !ssl->s3->initial_handshake_complete) { + if (!CBB_add_u16(&child, SSL3_CK_SCSV & 0xffff)) { + return 0; + } + } + + if (ssl->mode & SSL_MODE_SEND_FALLBACK_SCSV) { + if (!CBB_add_u16(&child, SSL3_CK_FALLBACK_SCSV & 0xffff)) { + return 0; + } + } + + return CBB_flush(out); +} + +int ssl_write_client_hello(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CLIENT_HELLO)) { + goto err; + } + + /* Renegotiations do not participate in session resumption. */ + int has_session = ssl->session != NULL && + !ssl->s3->initial_handshake_complete; + + CBB child; + if (!CBB_add_u16(&body, hs->client_version) || + !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) || + !CBB_add_u8_length_prefixed(&body, &child) || + (has_session && + !CBB_add_bytes(&child, ssl->session->session_id, + ssl->session->session_id_length))) { + goto err; + } + + if (SSL_is_dtls(ssl)) { + if (!CBB_add_u8_length_prefixed(&body, &child) || + !CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) { + goto err; + } + } + + size_t header_len = + SSL_is_dtls(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH; + if (!ssl_write_client_cipher_list(ssl, &body, min_version, max_version) || + !CBB_add_u8(&body, 1 /* one compression method */) || + !CBB_add_u8(&body, 0 /* null compression */) || + !ssl_add_clienthello_tlsext(hs, &body, header_len + CBB_len(&body))) { + goto err; + } + + uint8_t *msg = NULL; + size_t len; + if (!ssl->method->finish_message(ssl, &cbb, &msg, &len)) { + goto err; + } + + /* Now that the length prefixes have been computed, fill in the placeholder + * PSK binder. */ + if (hs->needs_psk_binder && + !tls13_write_psk_binder(hs, msg, len)) { + OPENSSL_free(msg); + goto err; + } + + return ssl->method->add_message(ssl, msg, len); + + err: + CBB_cleanup(&cbb); + return 0; +} + +static int ssl3_send_client_hello(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + /* The handshake buffer is reset on every ClientHello. Notably, in DTLS, we + * may send multiple ClientHellos if we receive HelloVerifyRequest. */ + if (!SSL_TRANSCRIPT_init(&hs->transcript)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return -1; + } + + uint16_t max_wire_version = ssl->method->version_to_wire(max_version); + assert(hs->state == SSL3_ST_CW_CLNT_HELLO_A); + if (!ssl->s3->have_version) { + ssl->version = max_wire_version; + } + + /* Always advertise the ClientHello version from the original maximum version, + * even on renegotiation. The static RSA key exchange uses this field, and + * some servers fail when it changes across handshakes. */ + hs->client_version = max_wire_version; + if (max_version >= TLS1_3_VERSION) { + hs->client_version = ssl->method->version_to_wire(TLS1_2_VERSION); + } + + /* If the configured session has expired or was created at a disabled + * version, drop it. */ + if (ssl->session != NULL) { + uint16_t session_version; + if (ssl->session->is_server || + !ssl->method->version_from_wire(&session_version, + ssl->session->ssl_version) || + (session_version < TLS1_3_VERSION && + ssl->session->session_id_length == 0) || + ssl->session->not_resumable || + !ssl_session_is_time_valid(ssl, ssl->session) || + session_version < min_version || session_version > max_version) { + ssl_set_session(ssl, NULL); + } + } + + /* If resending the ClientHello in DTLS after a HelloVerifyRequest, don't + * renegerate the client_random. The random must be reused. */ + if ((!SSL_is_dtls(ssl) || !ssl->d1->send_cookie) && + !RAND_bytes(ssl->s3->client_random, sizeof(ssl->s3->client_random))) { + return -1; + } + + if (!ssl_write_client_hello(hs)) { + return -1; + } + + return 1; +} + +static int dtls1_get_hello_verify(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int al; + CBS hello_verify_request, cookie; + uint16_t server_version; + + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (ssl->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) { + ssl->d1->send_cookie = 0; + ssl->s3->tmp.reuse_message = 1; + return 1; + } + + CBS_init(&hello_verify_request, ssl->init_msg, ssl->init_num); + if (!CBS_get_u16(&hello_verify_request, &server_version) || + !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) || + CBS_len(&hello_verify_request) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (CBS_len(&cookie) > sizeof(ssl->d1->cookie)) { + al = SSL_AD_ILLEGAL_PARAMETER; + goto f_err; + } + + OPENSSL_memcpy(ssl->d1->cookie, CBS_data(&cookie), CBS_len(&cookie)); + ssl->d1->cookie_len = CBS_len(&cookie); + + ssl->d1->send_cookie = 1; + return 1; + +f_err: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); + return -1; +} + +static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int al = SSL_AD_INTERNAL_ERROR; + CBS server_hello, server_random, session_id; + uint16_t server_wire_version, cipher_suite; + uint8_t compression_method; + + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + uint32_t err = ERR_peek_error(); + if (ERR_GET_LIB(err) == ERR_LIB_SSL && + ERR_GET_REASON(err) == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE) { + /* Add a dedicated error code to the queue for a handshake_failure alert + * in response to ClientHello. This matches NSS's client behavior and + * gives a better error on a (probable) failure to negotiate initial + * parameters. Note: this error code comes after the original one. + * + * See https://crbug.com/446505. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_HANDSHAKE_FAILURE_ON_CLIENT_HELLO); + } + return ret; + } + + if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO && + ssl->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); + return -1; + } + + CBS_init(&server_hello, ssl->init_msg, ssl->init_num); + + if (!CBS_get_u16(&server_hello, &server_wire_version)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + uint16_t min_version, max_version, server_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version) || + !ssl->method->version_from_wire(&server_version, server_wire_version) || + server_version < min_version || server_version > max_version) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL); + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + + assert(ssl->s3->have_version == ssl->s3->initial_handshake_complete); + if (!ssl->s3->have_version) { + ssl->version = server_wire_version; + /* At this point, the connection's version is known and ssl->version is + * fixed. Begin enforcing the record-layer version. */ + ssl->s3->have_version = 1; + } else if (server_wire_version != ssl->version) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION); + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + hs->state = SSL_ST_TLS13; + hs->do_tls13_handshake = tls13_client_handshake; + return 1; + } + + ssl_clear_tls13_state(hs); + + if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) { + return -1; + } + + if (!CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) || + !CBS_get_u8_length_prefixed(&server_hello, &session_id) || + CBS_len(&session_id) > SSL3_SESSION_ID_SIZE || + !CBS_get_u16(&server_hello, &cipher_suite) || + !CBS_get_u8(&server_hello, &compression_method)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + /* Copy over the server random. */ + OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE); + + /* TODO(davidben): Implement the TLS 1.1 and 1.2 downgrade sentinels once TLS + * 1.3 is finalized and we are not implementing a draft version. */ + + if (!ssl->s3->initial_handshake_complete && ssl->session != NULL && + ssl->session->session_id_length != 0 && + CBS_mem_equal(&session_id, ssl->session->session_id, + ssl->session->session_id_length)) { + ssl->s3->session_reused = 1; + } else { + /* The session wasn't resumed. Create a fresh SSL_SESSION to + * fill out. */ + ssl_set_session(ssl, NULL); + if (!ssl_get_new_session(hs, 0 /* client */)) { + goto f_err; + } + /* Note: session_id could be empty. */ + hs->new_session->session_id_length = CBS_len(&session_id); + OPENSSL_memcpy(hs->new_session->session_id, CBS_data(&session_id), + CBS_len(&session_id)); + } + + const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_suite); + if (c == NULL) { + /* unknown cipher */ + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED); + goto f_err; + } + + /* The cipher must be allowed in the selected version and enabled. */ + uint32_t mask_a, mask_k; + ssl_get_client_disabled(ssl, &mask_a, &mask_k); + if ((c->algorithm_mkey & mask_k) || (c->algorithm_auth & mask_a) || + SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) || + SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl) || + !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, c)) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED); + goto f_err; + } + + if (ssl->session != NULL) { + if (ssl->session->ssl_version != ssl->version) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED); + goto f_err; + } + if (ssl->session->cipher != c) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED); + goto f_err; + } + if (!ssl_session_is_context_valid(ssl, ssl->session)) { + /* This is actually a client application bug. */ + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, + SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT); + goto f_err; + } + } else { + hs->new_session->cipher = c; + } + hs->new_cipher = c; + + /* Now that the cipher is known, initialize the handshake hash and hash the + * ServerHello. */ + if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(ssl), + c->algorithm_prf) || + !ssl_hash_current_message(hs)) { + goto f_err; + } + + /* If doing a full handshake, the server may request a client certificate + * which requires hashing the handshake transcript. Otherwise, the handshake + * buffer may be released. */ + if (ssl->session != NULL || + !ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + } + + /* Only the NULL compression algorithm is supported. */ + if (compression_method != 0) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM); + goto f_err; + } + + /* TLS extensions */ + if (!ssl_parse_serverhello_tlsext(hs, &server_hello)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); + goto err; + } + + /* There should be nothing left over in the record. */ + if (CBS_len(&server_hello) != 0) { + /* wrong packet length */ + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (ssl->session != NULL && + hs->extended_master_secret != ssl->session->extended_master_secret) { + al = SSL_AD_HANDSHAKE_FAILURE; + if (ssl->session->extended_master_secret) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION); + } + goto f_err; + } + + return 1; + +f_err: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); +err: + return -1; +} + +static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) || + !ssl_hash_current_message(hs)) { + return -1; + } + + CBS cbs; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + + uint8_t alert = SSL_AD_DECODE_ERROR; + sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free); + EVP_PKEY_free(hs->peer_pubkey); + hs->peer_pubkey = NULL; + hs->new_session->certs = ssl_parse_cert_chain(&alert, &hs->peer_pubkey, NULL, + &cbs, ssl->ctx->pool); + if (hs->new_session->certs == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; + } + + if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0 || + CBS_len(&cbs) != 0 || + !ssl->ctx->x509_method->session_cache_objects(hs->new_session)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return -1; + } + + if (!ssl_check_leaf_certificate( + hs, hs->peer_pubkey, + sk_CRYPTO_BUFFER_value(hs->new_session->certs, 0))) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; + } + + /* Disallow the server certificate from changing during a renegotiation. See + * https://mitls.org/pages/attacks/3SHAKE. We never resume on renegotiation, + * so this check is sufficient. */ + if (ssl->s3->established_session != NULL) { + if (sk_CRYPTO_BUFFER_num(ssl->s3->established_session->certs) != + sk_CRYPTO_BUFFER_num(hs->new_session->certs)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_CERT_CHANGED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; + } + + for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(hs->new_session->certs); i++) { + const CRYPTO_BUFFER *old_cert = + sk_CRYPTO_BUFFER_value(ssl->s3->established_session->certs, i); + const CRYPTO_BUFFER *new_cert = + sk_CRYPTO_BUFFER_value(hs->new_session->certs, i); + if (CRYPTO_BUFFER_len(old_cert) != CRYPTO_BUFFER_len(new_cert) || + OPENSSL_memcmp(CRYPTO_BUFFER_data(old_cert), + CRYPTO_BUFFER_data(new_cert), + CRYPTO_BUFFER_len(old_cert)) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_CERT_CHANGED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; + } + } + } + + return 1; +} + +static int ssl3_get_cert_status(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int al; + CBS certificate_status, ocsp_response; + uint8_t status_type; + + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) { + /* A server may send status_request in ServerHello and then change + * its mind about sending CertificateStatus. */ + ssl->s3->tmp.reuse_message = 1; + return 1; + } + + if (!ssl_hash_current_message(hs)) { + return -1; + } + + CBS_init(&certificate_status, ssl->init_msg, ssl->init_num); + if (!CBS_get_u8(&certificate_status, &status_type) || + status_type != TLSEXT_STATUSTYPE_ocsp || + !CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) || + CBS_len(&ocsp_response) == 0 || + CBS_len(&certificate_status) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (!CBS_stow(&ocsp_response, &hs->new_session->ocsp_response, + &hs->new_session->ocsp_response_length)) { + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto f_err; + } + return 1; + +f_err: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); + return -1; +} + +static int ssl3_verify_server_cert(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl->ctx->x509_method->session_verify_cert_chain(hs->new_session, ssl)) { + return -1; + } + + return 1; +} + +static int ssl3_get_server_key_exchange(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int al; + DH *dh = NULL; + EC_KEY *ecdh = NULL; + EC_POINT *srvr_ecpoint = NULL; + + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE) { + /* Some ciphers (pure PSK) have an optional ServerKeyExchange message. */ + if (ssl_cipher_requires_server_key_exchange(hs->new_cipher)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return -1; + } + + ssl->s3->tmp.reuse_message = 1; + return 1; + } + + if (!ssl_hash_current_message(hs)) { + return -1; + } + + /* Retain a copy of the original CBS to compute the signature over. */ + CBS server_key_exchange; + CBS_init(&server_key_exchange, ssl->init_msg, ssl->init_num); + CBS server_key_exchange_orig = server_key_exchange; + + uint32_t alg_k = hs->new_cipher->algorithm_mkey; + uint32_t alg_a = hs->new_cipher->algorithm_auth; + + if (alg_a & SSL_aPSK) { + CBS psk_identity_hint; + + /* Each of the PSK key exchanges begins with a psk_identity_hint. */ + if (!CBS_get_u16_length_prefixed(&server_key_exchange, + &psk_identity_hint)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + /* Store PSK identity hint for later use, hint is used in + * ssl3_send_client_key_exchange. Assume that the maximum length of a PSK + * identity hint can be as long as the maximum length of a PSK identity. + * Also do not allow NULL characters; identities are saved as C strings. + * + * TODO(davidben): Should invalid hints be ignored? It's a hint rather than + * a specific identity. */ + if (CBS_len(&psk_identity_hint) > PSK_MAX_IDENTITY_LEN || + CBS_contains_zero_byte(&psk_identity_hint)) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + goto f_err; + } + + /* Save non-empty identity hints as a C string. Empty identity hints we + * treat as missing. Plain PSK makes it possible to send either no hint + * (omit ServerKeyExchange) or an empty hint, while ECDHE_PSK can only spell + * empty hint. Having different capabilities is odd, so we interpret empty + * and missing as identical. */ + if (CBS_len(&psk_identity_hint) != 0 && + !CBS_strdup(&psk_identity_hint, &hs->peer_psk_identity_hint)) { + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto f_err; + } + } + + if (alg_k & SSL_kDHE) { + CBS dh_p, dh_g, dh_Ys; + if (!CBS_get_u16_length_prefixed(&server_key_exchange, &dh_p) || + CBS_len(&dh_p) == 0 || + !CBS_get_u16_length_prefixed(&server_key_exchange, &dh_g) || + CBS_len(&dh_g) == 0 || + !CBS_get_u16_length_prefixed(&server_key_exchange, &dh_Ys) || + CBS_len(&dh_Ys) == 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + dh = DH_new(); + if (dh == NULL) { + goto err; + } + + dh->p = BN_bin2bn(CBS_data(&dh_p), CBS_len(&dh_p), NULL); + dh->g = BN_bin2bn(CBS_data(&dh_g), CBS_len(&dh_g), NULL); + if (dh->p == NULL || dh->g == NULL) { + goto err; + } + + unsigned bits = DH_num_bits(dh); + if (bits < 1024) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_DH_P_LENGTH); + goto err; + } else if (bits > 4096) { + /* Overly large DHE groups are prohibitively expensive, so enforce a limit + * to prevent a server from causing us to perform too expensive of a + * computation. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_DH_P_TOO_LONG); + goto err; + } + + SSL_ECDH_CTX_init_for_dhe(&hs->ecdh_ctx, dh); + dh = NULL; + + /* Save the peer public key for later. */ + if (!CBS_stow(&dh_Ys, &hs->peer_key, &hs->peer_key_len)) { + goto err; + } + } else if (alg_k & SSL_kECDHE) { + /* Parse the server parameters. */ + uint8_t group_type; + uint16_t group_id; + CBS point; + if (!CBS_get_u8(&server_key_exchange, &group_type) || + group_type != NAMED_CURVE_TYPE || + !CBS_get_u16(&server_key_exchange, &group_id) || + !CBS_get_u8_length_prefixed(&server_key_exchange, &point)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + hs->new_session->group_id = group_id; + + /* Ensure the group is consistent with preferences. */ + if (!tls1_check_group_id(ssl, group_id)) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE); + goto f_err; + } + + /* Initialize ECDH and save the peer public key for later. */ + if (!SSL_ECDH_CTX_init(&hs->ecdh_ctx, group_id) || + !CBS_stow(&point, &hs->peer_key, &hs->peer_key_len)) { + goto err; + } + } else if (!(alg_k & SSL_kPSK)) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); + goto f_err; + } + + /* At this point, |server_key_exchange| contains the signature, if any, while + * |server_key_exchange_orig| contains the entire message. From that, derive + * a CBS containing just the parameter. */ + CBS parameter; + CBS_init(¶meter, CBS_data(&server_key_exchange_orig), + CBS_len(&server_key_exchange_orig) - CBS_len(&server_key_exchange)); + + /* ServerKeyExchange should be signed by the server's public key. */ + if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + uint16_t signature_algorithm = 0; + if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { + if (!CBS_get_u16(&server_key_exchange, &signature_algorithm)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + if (!tls12_check_peer_sigalg(ssl, &al, signature_algorithm)) { + goto f_err; + } + hs->new_session->peer_signature_algorithm = signature_algorithm; + } else if (hs->peer_pubkey->type == EVP_PKEY_RSA) { + signature_algorithm = SSL_SIGN_RSA_PKCS1_MD5_SHA1; + } else if (hs->peer_pubkey->type == EVP_PKEY_EC) { + signature_algorithm = SSL_SIGN_ECDSA_SHA1; + } else { + al = SSL_AD_UNSUPPORTED_CERTIFICATE; + OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE); + goto f_err; + } + + /* The last field in |server_key_exchange| is the signature. */ + CBS signature; + if (!CBS_get_u16_length_prefixed(&server_key_exchange, &signature) || + CBS_len(&server_key_exchange) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + CBB transcript; + uint8_t *transcript_data; + size_t transcript_len; + if (!CBB_init(&transcript, 2*SSL3_RANDOM_SIZE + CBS_len(¶meter)) || + !CBB_add_bytes(&transcript, ssl->s3->client_random, SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&transcript, ssl->s3->server_random, SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&transcript, CBS_data(¶meter), CBS_len(¶meter)) || + !CBB_finish(&transcript, &transcript_data, &transcript_len)) { + CBB_cleanup(&transcript); + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto f_err; + } + + int sig_ok = ssl_public_key_verify( + ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm, + hs->peer_pubkey, transcript_data, transcript_len); + OPENSSL_free(transcript_data); + +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + sig_ok = 1; + ERR_clear_error(); +#endif + if (!sig_ok) { + /* bad signature */ + al = SSL_AD_DECRYPT_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE); + goto f_err; + } + } else { + /* PSK ciphers are the only supported certificate-less ciphers. */ + assert(alg_a == SSL_aPSK); + + if (CBS_len(&server_key_exchange) > 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_EXTRA_DATA_IN_MESSAGE); + goto f_err; + } + } + return 1; + +f_err: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); +err: + DH_free(dh); + EC_POINT_free(srvr_ecpoint); + EC_KEY_free(ecdh); + return -1; +} + +static int ssl3_get_certificate_request(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int msg_ret = ssl->method->ssl_get_message(ssl); + if (msg_ret <= 0) { + return msg_ret; + } + + if (ssl->s3->tmp.message_type == SSL3_MT_SERVER_HELLO_DONE) { + ssl->s3->tmp.reuse_message = 1; + /* If we get here we don't need the handshake buffer as we won't be doing + * client auth. */ + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + return 1; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_REQUEST) || + !ssl_hash_current_message(hs)) { + return -1; + } + + CBS cbs; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + + /* Get the certificate types. */ + CBS certificate_types; + if (!CBS_get_u8_length_prefixed(&cbs, &certificate_types)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return -1; + } + + if (!CBS_stow(&certificate_types, &hs->certificate_types, + &hs->num_certificate_types)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return -1; + } + + if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { + CBS supported_signature_algorithms; + if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) || + !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return -1; + } + } + + uint8_t alert = SSL_AD_DECODE_ERROR; + STACK_OF(CRYPTO_BUFFER) *ca_names = + ssl_parse_client_CA_list(ssl, &alert, &cbs); + if (ca_names == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; + } + + if (CBS_len(&cbs) != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + sk_CRYPTO_BUFFER_pop_free(ca_names, CRYPTO_BUFFER_free); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return -1; + } + + hs->cert_request = 1; + sk_CRYPTO_BUFFER_pop_free(hs->ca_names, CRYPTO_BUFFER_free); + hs->ca_names = ca_names; + ssl->ctx->x509_method->hs_flush_cached_ca_names(hs); + return 1; +} + +static int ssl3_get_server_hello_done(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO_DONE) || + !ssl_hash_current_message(hs)) { + return -1; + } + + /* ServerHelloDone is empty. */ + if (ssl->init_num > 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return -1; + } + + return 1; +} + +static int ssl3_send_client_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + /* Call cert_cb to update the certificate. */ + if (ssl->cert->cert_cb) { + int ret = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg); + if (ret < 0) { + ssl->rwstate = SSL_X509_LOOKUP; + return -1; + } + if (ret == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return -1; + } + } + + if (!ssl_has_certificate(ssl)) { + /* Without a client certificate, the handshake buffer may be released. */ + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + + /* In SSL 3.0, the Certificate message is replaced with a warning alert. */ + if (ssl->version == SSL3_VERSION) { + if (!ssl->method->add_alert(ssl, SSL3_AL_WARNING, + SSL_AD_NO_CERTIFICATE)) { + return -1; + } + return 1; + } + } + + if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(ssl) || + !ssl3_output_cert_chain(ssl)) { + return -1; + } + return 1; +} + +OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned), + SIZE_T_IS_SMALLER_THAN_UNSIGNED); + +static int ssl3_send_client_key_exchange(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + uint8_t *pms = NULL; + size_t pms_len = 0; + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CLIENT_KEY_EXCHANGE)) { + goto err; + } + + uint32_t alg_k = hs->new_cipher->algorithm_mkey; + uint32_t alg_a = hs->new_cipher->algorithm_auth; + + /* If using a PSK key exchange, prepare the pre-shared key. */ + unsigned psk_len = 0; + uint8_t psk[PSK_MAX_PSK_LEN]; + if (alg_a & SSL_aPSK) { + if (ssl->psk_client_callback == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_CLIENT_CB); + goto err; + } + + char identity[PSK_MAX_IDENTITY_LEN + 1]; + OPENSSL_memset(identity, 0, sizeof(identity)); + psk_len = + ssl->psk_client_callback(ssl, hs->peer_psk_identity_hint, identity, + sizeof(identity), psk, sizeof(psk)); + if (psk_len == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + goto err; + } + assert(psk_len <= PSK_MAX_PSK_LEN); + + OPENSSL_free(hs->new_session->psk_identity); + hs->new_session->psk_identity = BUF_strdup(identity); + if (hs->new_session->psk_identity == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* Write out psk_identity. */ + CBB child; + if (!CBB_add_u16_length_prefixed(&body, &child) || + !CBB_add_bytes(&child, (const uint8_t *)identity, + OPENSSL_strnlen(identity, sizeof(identity))) || + !CBB_flush(&body)) { + goto err; + } + } + + /* Depending on the key exchange method, compute |pms| and |pms_len|. */ + if (alg_k & SSL_kRSA) { + pms_len = SSL_MAX_MASTER_KEY_LENGTH; + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + RSA *rsa = EVP_PKEY_get0_RSA(hs->peer_pubkey); + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + + pms[0] = hs->client_version >> 8; + pms[1] = hs->client_version & 0xff; + if (!RAND_bytes(&pms[2], SSL_MAX_MASTER_KEY_LENGTH - 2)) { + goto err; + } + + CBB child, *enc_pms = &body; + size_t enc_pms_len; + /* In TLS, there is a length prefix. */ + if (ssl->version > SSL3_VERSION) { + if (!CBB_add_u16_length_prefixed(&body, &child)) { + goto err; + } + enc_pms = &child; + } + + uint8_t *ptr; + if (!CBB_reserve(enc_pms, &ptr, RSA_size(rsa)) || + !RSA_encrypt(rsa, &enc_pms_len, ptr, RSA_size(rsa), pms, pms_len, + RSA_PKCS1_PADDING) || + !CBB_did_write(enc_pms, enc_pms_len) || + !CBB_flush(&body)) { + goto err; + } + } else if (alg_k & (SSL_kECDHE|SSL_kDHE)) { + /* Generate a keypair and serialize the public half. */ + CBB child; + if (!SSL_ECDH_CTX_add_key(&hs->ecdh_ctx, &body, &child)) { + goto err; + } + + /* Compute the premaster. */ + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!SSL_ECDH_CTX_accept(&hs->ecdh_ctx, &child, &pms, &pms_len, &alert, + hs->peer_key, hs->peer_key_len)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + goto err; + } + if (!CBB_flush(&body)) { + goto err; + } + + /* The key exchange state may now be discarded. */ + SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx); + OPENSSL_free(hs->peer_key); + hs->peer_key = NULL; + hs->peer_key_len = 0; + } else if (alg_k & SSL_kPSK) { + /* For plain PSK, other_secret is a block of 0s with the same length as + * the pre-shared key. */ + pms_len = psk_len; + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + OPENSSL_memset(pms, 0, pms_len); + } else { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* For a PSK cipher suite, other_secret is combined with the pre-shared + * key. */ + if (alg_a & SSL_aPSK) { + CBB pms_cbb, child; + uint8_t *new_pms; + size_t new_pms_len; + + CBB_zero(&pms_cbb); + if (!CBB_init(&pms_cbb, 2 + psk_len + 2 + pms_len) || + !CBB_add_u16_length_prefixed(&pms_cbb, &child) || + !CBB_add_bytes(&child, pms, pms_len) || + !CBB_add_u16_length_prefixed(&pms_cbb, &child) || + !CBB_add_bytes(&child, psk, psk_len) || + !CBB_finish(&pms_cbb, &new_pms, &new_pms_len)) { + CBB_cleanup(&pms_cbb); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + OPENSSL_cleanse(pms, pms_len); + OPENSSL_free(pms); + pms = new_pms; + pms_len = new_pms_len; + } + + /* The message must be added to the finished hash before calculating the + * master secret. */ + if (!ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + hs->new_session->master_key_length = tls1_generate_master_secret( + hs, hs->new_session->master_key, pms, pms_len); + if (hs->new_session->master_key_length == 0) { + goto err; + } + hs->new_session->extended_master_secret = hs->extended_master_secret; + OPENSSL_cleanse(pms, pms_len); + OPENSSL_free(pms); + + return 1; + +err: + CBB_cleanup(&cbb); + if (pms != NULL) { + OPENSSL_cleanse(pms, pms_len); + OPENSSL_free(pms); + } + return -1; +} + +static int ssl3_send_cert_verify(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + assert(ssl_has_private_key(ssl)); + + CBB cbb, body, child; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CERTIFICATE_VERIFY)) { + goto err; + } + + uint16_t signature_algorithm; + if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) { + goto err; + } + if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { + /* Write out the digest type in TLS 1.2. */ + if (!CBB_add_u16(&body, signature_algorithm)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + /* Set aside space for the signature. */ + const size_t max_sig_len = ssl_private_key_max_signature_len(ssl); + uint8_t *ptr; + if (!CBB_add_u16_length_prefixed(&body, &child) || + !CBB_reserve(&child, &ptr, max_sig_len)) { + goto err; + } + + size_t sig_len = max_sig_len; + enum ssl_private_key_result_t sign_result; + if (hs->state == SSL3_ST_CW_CERT_VRFY_A) { + /* The SSL3 construction for CertificateVerify does not decompose into a + * single final digest and signature, and must be special-cased. */ + if (ssl3_protocol_version(ssl) == SSL3_VERSION) { + if (ssl->cert->key_method != NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY); + goto err; + } + + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_len; + if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(&hs->transcript, digest, + &digest_len, hs->new_session, + signature_algorithm)) { + goto err; + } + + sign_result = ssl_private_key_success; + + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(ssl->cert->privatekey, NULL); + if (pctx == NULL || + !EVP_PKEY_sign_init(pctx) || + !EVP_PKEY_sign(pctx, ptr, &sig_len, digest, digest_len)) { + EVP_PKEY_CTX_free(pctx); + sign_result = ssl_private_key_failure; + goto err; + } + EVP_PKEY_CTX_free(pctx); + } else { + sign_result = ssl_private_key_sign( + ssl, ptr, &sig_len, max_sig_len, signature_algorithm, + (const uint8_t *)hs->transcript.buffer->data, + hs->transcript.buffer->length); + } + + /* The handshake buffer is no longer necessary. */ + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + } else { + assert(hs->state == SSL3_ST_CW_CERT_VRFY_B); + sign_result = ssl_private_key_complete(ssl, ptr, &sig_len, max_sig_len); + } + + switch (sign_result) { + case ssl_private_key_success: + break; + case ssl_private_key_failure: + goto err; + case ssl_private_key_retry: + ssl->rwstate = SSL_PRIVATE_KEY_OPERATION; + hs->state = SSL3_ST_CW_CERT_VRFY_B; + goto err; + } + + if (!CBB_did_write(&child, sig_len) || + !ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + return 1; + +err: + CBB_cleanup(&cbb); + return -1; +} + +static int ssl3_send_next_proto(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + static const uint8_t kZero[32] = {0}; + size_t padding_len = 32 - ((ssl->s3->next_proto_negotiated_len + 2) % 32); + + CBB cbb, body, child; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEXT_PROTO) || + !CBB_add_u8_length_prefixed(&body, &child) || + !CBB_add_bytes(&child, ssl->s3->next_proto_negotiated, + ssl->s3->next_proto_negotiated_len) || + !CBB_add_u8_length_prefixed(&body, &child) || + !CBB_add_bytes(&child, kZero, padding_len) || + !ssl_add_message_cbb(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + return 1; +} + +static int ssl3_send_channel_id(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_do_channel_id_callback(ssl)) { + return -1; + } + + if (ssl->tlsext_channel_id_private == NULL) { + ssl->rwstate = SSL_CHANNEL_ID_LOOKUP; + return -1; + } + + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) || + !tls1_write_channel_id(hs, &body) || + !ssl_add_message_cbb(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + return 1; +} + +static int ssl3_get_new_session_ticket(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_NEW_SESSION_TICKET) || + !ssl_hash_current_message(hs)) { + return -1; + } + + CBS new_session_ticket, ticket; + uint32_t tlsext_tick_lifetime_hint; + CBS_init(&new_session_ticket, ssl->init_msg, ssl->init_num); + if (!CBS_get_u32(&new_session_ticket, &tlsext_tick_lifetime_hint) || + !CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) || + CBS_len(&new_session_ticket) != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return -1; + } + + if (CBS_len(&ticket) == 0) { + /* RFC 5077 allows a server to change its mind and send no ticket after + * negotiating the extension. The value of |ticket_expected| is checked in + * |ssl_update_cache| so is cleared here to avoid an unnecessary update. */ + hs->ticket_expected = 0; + return 1; + } + + int session_renewed = ssl->session != NULL; + SSL_SESSION *session = hs->new_session; + if (session_renewed) { + /* The server is sending a new ticket for an existing session. Sessions are + * immutable once established, so duplicate all but the ticket of the + * existing session. */ + session = SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH); + if (session == NULL) { + /* This should never happen. */ + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + /* |tlsext_tick_lifetime_hint| is measured from when the ticket was issued. */ + ssl_session_rebase_time(ssl, session); + + if (!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + session->tlsext_tick_lifetime_hint = tlsext_tick_lifetime_hint; + + /* Generate a session ID for this session based on the session ticket. We use + * the session ID mechanism for detecting ticket resumption. This also fits in + * with assumptions elsewhere in OpenSSL.*/ + if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), + session->session_id, &session->session_id_length, + EVP_sha256(), NULL)) { + goto err; + } + + if (session_renewed) { + session->not_resumable = 0; + SSL_SESSION_free(ssl->session); + ssl->session = session; + } + + return 1; + +err: + if (session_renewed) { + SSL_SESSION_free(session); + } + return -1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/handshake_server.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/handshake_server.c new file mode 100644 index 000000000..81e45ef07 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/handshake_server.c @@ -0,0 +1,1953 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * Portions of the attached software ("Contribution") are developed by + * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project. + * + * The Contribution is licensed pursuant to the OpenSSL open source + * license provided above. + * + * ECC cipher suite support in OpenSSL originally written by + * Vipul Gupta and Sumit Gupta of Sun Microsystems Laboratories. + * + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bn.h> +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/cipher.h> +#include <openssl/dh.h> +#include <openssl/ec.h> +#include <openssl/ecdsa.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/nid.h> +#include <openssl/rand.h> +#include <openssl/x509.h> + +#include "internal.h" +#include "../crypto/internal.h" + + +static int ssl3_process_client_hello(SSL_HANDSHAKE *hs); +static int ssl3_select_certificate(SSL_HANDSHAKE *hs); +static int ssl3_select_parameters(SSL_HANDSHAKE *hs); +static int ssl3_send_server_hello(SSL_HANDSHAKE *hs); +static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs); +static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs); +static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs); +static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs); +static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs); +static int ssl3_get_client_certificate(SSL_HANDSHAKE *hs); +static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs); +static int ssl3_get_cert_verify(SSL_HANDSHAKE *hs); +static int ssl3_get_next_proto(SSL_HANDSHAKE *hs); +static int ssl3_get_channel_id(SSL_HANDSHAKE *hs); +static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs); + +static struct CRYPTO_STATIC_MUTEX g_v2clienthello_lock = + CRYPTO_STATIC_MUTEX_INIT; +static uint64_t g_v2clienthello_count = 0; + +uint64_t SSL_get_v2clienthello_count(void) { + CRYPTO_STATIC_MUTEX_lock_read(&g_v2clienthello_lock); + uint64_t ret = g_v2clienthello_count; + CRYPTO_STATIC_MUTEX_unlock_read(&g_v2clienthello_lock); + return ret; +} + +int ssl3_accept(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + uint32_t alg_a; + int ret = -1; + + assert(ssl->handshake_func == ssl3_accept); + assert(ssl->server); + + for (;;) { + int state = hs->state; + + switch (hs->state) { + case SSL_ST_INIT: + ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1); + hs->state = SSL3_ST_SR_CLNT_HELLO_A; + break; + + case SSL3_ST_SR_CLNT_HELLO_A: + ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + goto end; + } + hs->state = SSL3_ST_SR_CLNT_HELLO_B; + break; + + case SSL3_ST_SR_CLNT_HELLO_B: + ret = ssl3_process_client_hello(hs); + if (ret <= 0) { + goto end; + } + hs->state = SSL3_ST_SR_CLNT_HELLO_C; + break; + + case SSL3_ST_SR_CLNT_HELLO_C: + ret = ssl3_select_certificate(hs); + if (ret <= 0) { + goto end; + } + if (hs->state != SSL_ST_TLS13) { + hs->state = SSL3_ST_SR_CLNT_HELLO_D; + } + break; + + case SSL3_ST_SR_CLNT_HELLO_D: + ret = ssl3_select_parameters(hs); + if (ret <= 0) { + goto end; + } + ssl->method->received_flight(ssl); + hs->state = SSL3_ST_SW_SRVR_HELLO_A; + break; + + case SSL3_ST_SW_SRVR_HELLO_A: + ret = ssl3_send_server_hello(hs); + if (ret <= 0) { + goto end; + } + if (ssl->session != NULL) { + hs->state = SSL3_ST_SW_SESSION_TICKET_A; + } else { + hs->state = SSL3_ST_SW_CERT_A; + } + break; + + case SSL3_ST_SW_CERT_A: + if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + ret = ssl3_send_server_certificate(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_SW_CERT_STATUS_A; + break; + + case SSL3_ST_SW_CERT_STATUS_A: + if (hs->certificate_status_expected) { + ret = ssl3_send_certificate_status(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_SW_KEY_EXCH_A; + break; + + case SSL3_ST_SW_KEY_EXCH_A: + case SSL3_ST_SW_KEY_EXCH_B: + alg_a = hs->new_cipher->algorithm_auth; + + /* PSK ciphers send ServerKeyExchange if there is an identity hint. */ + if (ssl_cipher_requires_server_key_exchange(hs->new_cipher) || + ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) { + ret = ssl3_send_server_key_exchange(hs); + if (ret <= 0) { + goto end; + } + } + + hs->state = SSL3_ST_SW_CERT_REQ_A; + break; + + case SSL3_ST_SW_CERT_REQ_A: + if (hs->cert_request) { + ret = ssl3_send_certificate_request(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_SW_SRVR_DONE_A; + break; + + case SSL3_ST_SW_SRVR_DONE_A: + ret = ssl3_send_server_hello_done(hs); + if (ret <= 0) { + goto end; + } + hs->next_state = SSL3_ST_SR_CERT_A; + hs->state = SSL3_ST_SW_FLUSH; + break; + + case SSL3_ST_SR_CERT_A: + if (hs->cert_request) { + ret = ssl3_get_client_certificate(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_SR_KEY_EXCH_A; + break; + + case SSL3_ST_SR_KEY_EXCH_A: + case SSL3_ST_SR_KEY_EXCH_B: + ret = ssl3_get_client_key_exchange(hs); + if (ret <= 0) { + goto end; + } + hs->state = SSL3_ST_SR_CERT_VRFY_A; + break; + + case SSL3_ST_SR_CERT_VRFY_A: + ret = ssl3_get_cert_verify(hs); + if (ret <= 0) { + goto end; + } + + hs->state = SSL3_ST_SR_CHANGE; + break; + + case SSL3_ST_SR_CHANGE: + ret = ssl->method->read_change_cipher_spec(ssl); + if (ret <= 0) { + goto end; + } + + if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_READ)) { + ret = -1; + goto end; + } + + hs->state = SSL3_ST_SR_NEXT_PROTO_A; + break; + + case SSL3_ST_SR_NEXT_PROTO_A: + if (hs->next_proto_neg_seen) { + ret = ssl3_get_next_proto(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_SR_CHANNEL_ID_A; + break; + + case SSL3_ST_SR_CHANNEL_ID_A: + if (ssl->s3->tlsext_channel_id_valid) { + ret = ssl3_get_channel_id(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_SR_FINISHED_A; + break; + + case SSL3_ST_SR_FINISHED_A: + ret = ssl3_get_finished(hs); + if (ret <= 0) { + goto end; + } + + ssl->method->received_flight(ssl); + if (ssl->session != NULL) { + hs->state = SSL_ST_OK; + } else { + hs->state = SSL3_ST_SW_SESSION_TICKET_A; + } + + /* If this is a full handshake with ChannelID then record the handshake + * hashes in |hs->new_session| in case we need them to verify a + * ChannelID signature on a resumption of this session in the future. */ + if (ssl->session == NULL && ssl->s3->tlsext_channel_id_valid) { + ret = tls1_record_handshake_hashes_for_channel_id(hs); + if (ret <= 0) { + goto end; + } + } + break; + + case SSL3_ST_SW_SESSION_TICKET_A: + if (hs->ticket_expected) { + ret = ssl3_send_new_session_ticket(hs); + if (ret <= 0) { + goto end; + } + } + hs->state = SSL3_ST_SW_CHANGE; + break; + + case SSL3_ST_SW_CHANGE: + if (!ssl->method->add_change_cipher_spec(ssl) || + !tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_WRITE)) { + ret = -1; + goto end; + } + + hs->state = SSL3_ST_SW_FINISHED_A; + break; + + case SSL3_ST_SW_FINISHED_A: + ret = ssl3_send_finished(hs); + if (ret <= 0) { + goto end; + } + hs->state = SSL3_ST_SW_FLUSH; + if (ssl->session != NULL) { + hs->next_state = SSL3_ST_SR_CHANGE; + } else { + hs->next_state = SSL_ST_OK; + } + break; + + case SSL3_ST_SW_FLUSH: + ret = ssl->method->flush_flight(ssl); + if (ret <= 0) { + goto end; + } + + hs->state = hs->next_state; + if (hs->state != SSL_ST_OK) { + ssl->method->expect_flight(ssl); + } + break; + + case SSL_ST_TLS13: + ret = tls13_handshake(hs); + if (ret <= 0) { + goto end; + } + hs->state = SSL_ST_OK; + break; + + case SSL_ST_OK: + ssl->method->release_current_message(ssl, 1 /* free_buffer */); + + /* If we aren't retaining peer certificates then we can discard it + * now. */ + if (hs->new_session != NULL && + ssl->retain_only_sha256_of_client_certs) { + sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free); + hs->new_session->certs = NULL; + ssl->ctx->x509_method->session_clear(hs->new_session); + } + + SSL_SESSION_free(ssl->s3->established_session); + if (ssl->session != NULL) { + SSL_SESSION_up_ref(ssl->session); + ssl->s3->established_session = ssl->session; + } else { + ssl->s3->established_session = hs->new_session; + ssl->s3->established_session->not_resumable = 0; + hs->new_session = NULL; + } + + if (hs->v2_clienthello) { + CRYPTO_STATIC_MUTEX_lock_write(&g_v2clienthello_lock); + g_v2clienthello_count++; + CRYPTO_STATIC_MUTEX_unlock_write(&g_v2clienthello_lock); + } + + ssl->s3->initial_handshake_complete = 1; + ssl_update_cache(hs, SSL_SESS_CACHE_SERVER); + + ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1); + ret = 1; + goto end; + + default: + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE); + ret = -1; + goto end; + } + + if (hs->state != state) { + ssl_do_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 1); + } + } + +end: + ssl_do_info_callback(ssl, SSL_CB_ACCEPT_EXIT, ret); + return ret; +} + +int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello, + uint16_t id) { + CBS cipher_suites; + CBS_init(&cipher_suites, client_hello->cipher_suites, + client_hello->cipher_suites_len); + + while (CBS_len(&cipher_suites) > 0) { + uint16_t got_id; + if (!CBS_get_u16(&cipher_suites, &got_id)) { + return 0; + } + + if (got_id == id) { + return 1; + } + } + + return 0; +} + +static int negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert, + const SSL_CLIENT_HELLO *client_hello) { + SSL *const ssl = hs->ssl; + assert(!ssl->s3->have_version); + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + *out_alert = SSL_AD_PROTOCOL_VERSION; + return 0; + } + + uint16_t version = 0; + /* Check supported_versions extension if it is present. */ + CBS supported_versions; + if (ssl_client_hello_get_extension(client_hello, &supported_versions, + TLSEXT_TYPE_supported_versions)) { + CBS versions; + if (!CBS_get_u8_length_prefixed(&supported_versions, &versions) || + CBS_len(&supported_versions) != 0 || + CBS_len(&versions) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Choose the newest commonly-supported version advertised by the client. + * The client orders the versions according to its preferences, but we're + * not required to honor the client's preferences. */ + int found_version = 0; + while (CBS_len(&versions) != 0) { + uint16_t ext_version; + if (!CBS_get_u16(&versions, &ext_version)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + if (!ssl->method->version_from_wire(&ext_version, ext_version)) { + continue; + } + if (min_version <= ext_version && + ext_version <= max_version && + (!found_version || version < ext_version)) { + version = ext_version; + found_version = 1; + } + } + + if (!found_version) { + goto unsupported_protocol; + } + } else { + /* Process ClientHello.version instead. Note that versions beyond (D)TLS 1.2 + * do not use this mechanism. */ + if (SSL_is_dtls(ssl)) { + if (client_hello->version <= DTLS1_2_VERSION) { + version = TLS1_2_VERSION; + } else if (client_hello->version <= DTLS1_VERSION) { + version = TLS1_1_VERSION; + } else { + goto unsupported_protocol; + } + } else { + if (client_hello->version >= TLS1_2_VERSION) { + version = TLS1_2_VERSION; + } else if (client_hello->version >= TLS1_1_VERSION) { + version = TLS1_1_VERSION; + } else if (client_hello->version >= TLS1_VERSION) { + version = TLS1_VERSION; + } else if (client_hello->version >= SSL3_VERSION) { + version = SSL3_VERSION; + } else { + goto unsupported_protocol; + } + } + + /* Apply our minimum and maximum version. */ + if (version > max_version) { + version = max_version; + } + + if (version < min_version) { + goto unsupported_protocol; + } + } + + /* Handle FALLBACK_SCSV. */ + if (ssl_client_cipher_list_contains_cipher(client_hello, + SSL3_CK_FALLBACK_SCSV & 0xffff) && + version < max_version) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK); + *out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK; + return 0; + } + + hs->client_version = client_hello->version; + ssl->version = ssl->method->version_to_wire(version); + + /* At this point, the connection's version is known and |ssl->version| is + * fixed. Begin enforcing the record-layer version. */ + ssl->s3->have_version = 1; + + return 1; + +unsupported_protocol: + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL); + *out_alert = SSL_AD_PROTOCOL_VERSION; + return 0; +} + +static STACK_OF(SSL_CIPHER) * + ssl_parse_client_cipher_list(const SSL_CLIENT_HELLO *client_hello) { + CBS cipher_suites; + CBS_init(&cipher_suites, client_hello->cipher_suites, + client_hello->cipher_suites_len); + + STACK_OF(SSL_CIPHER) *sk = sk_SSL_CIPHER_new_null(); + if (sk == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + while (CBS_len(&cipher_suites) > 0) { + uint16_t cipher_suite; + + if (!CBS_get_u16(&cipher_suites, &cipher_suite)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST); + goto err; + } + + const SSL_CIPHER *c = SSL_get_cipher_by_value(cipher_suite); + if (c != NULL && !sk_SSL_CIPHER_push(sk, c)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + return sk; + +err: + sk_SSL_CIPHER_free(sk); + return NULL; +} + +/* ssl_get_compatible_server_ciphers determines the key exchange and + * authentication cipher suite masks compatible with the server configuration + * and current ClientHello parameters of |hs|. It sets |*out_mask_k| to the key + * exchange mask and |*out_mask_a| to the authentication mask. */ +static void ssl_get_compatible_server_ciphers(SSL_HANDSHAKE *hs, + uint32_t *out_mask_k, + uint32_t *out_mask_a) { + SSL *const ssl = hs->ssl; + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + *out_mask_k = SSL_kGENERIC; + *out_mask_a = SSL_aGENERIC; + return; + } + + uint32_t mask_k = 0; + uint32_t mask_a = 0; + + if (ssl_has_certificate(ssl)) { + int type = ssl_private_key_type(ssl); + if (type == NID_rsaEncryption) { + mask_k |= SSL_kRSA; + mask_a |= SSL_aRSA; + } else if (ssl_is_ecdsa_key_type(type)) { + mask_a |= SSL_aECDSA; + } + } + + if (ssl->cert->dh_tmp != NULL || ssl->cert->dh_tmp_cb != NULL) { + mask_k |= SSL_kDHE; + } + + /* Check for a shared group to consider ECDHE ciphers. */ + uint16_t unused; + if (tls1_get_shared_group(hs, &unused)) { + mask_k |= SSL_kECDHE; + } + + /* PSK requires a server callback. */ + if (ssl->psk_server_callback != NULL) { + mask_k |= SSL_kPSK; + mask_a |= SSL_aPSK; + } + + *out_mask_k = mask_k; + *out_mask_a = mask_a; +} + +static const SSL_CIPHER *ssl3_choose_cipher( + SSL_HANDSHAKE *hs, const SSL_CLIENT_HELLO *client_hello, + const struct ssl_cipher_preference_list_st *server_pref) { + SSL *const ssl = hs->ssl; + const SSL_CIPHER *c, *ret = NULL; + STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow; + int ok; + size_t cipher_index; + uint32_t alg_k, alg_a, mask_k, mask_a; + /* in_group_flags will either be NULL, or will point to an array of bytes + * which indicate equal-preference groups in the |prio| stack. See the + * comment about |in_group_flags| in the |ssl_cipher_preference_list_st| + * struct. */ + const uint8_t *in_group_flags; + /* group_min contains the minimal index so far found in a group, or -1 if no + * such value exists yet. */ + int group_min = -1; + + STACK_OF(SSL_CIPHER) *clnt = ssl_parse_client_cipher_list(client_hello); + if (clnt == NULL) { + return NULL; + } + + if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) { + prio = srvr; + in_group_flags = server_pref->in_group_flags; + allow = clnt; + } else { + prio = clnt; + in_group_flags = NULL; + allow = srvr; + } + + ssl_get_compatible_server_ciphers(hs, &mask_k, &mask_a); + + for (size_t i = 0; i < sk_SSL_CIPHER_num(prio); i++) { + c = sk_SSL_CIPHER_value(prio, i); + + ok = 1; + + /* Check the TLS version. */ + if (SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) || + SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl)) { + ok = 0; + } + + alg_k = c->algorithm_mkey; + alg_a = c->algorithm_auth; + + ok = ok && (alg_k & mask_k) && (alg_a & mask_a); + + if (ok && sk_SSL_CIPHER_find(allow, &cipher_index, c)) { + if (in_group_flags != NULL && in_group_flags[i] == 1) { + /* This element of |prio| is in a group. Update the minimum index found + * so far and continue looking. */ + if (group_min == -1 || (size_t)group_min > cipher_index) { + group_min = cipher_index; + } + } else { + if (group_min != -1 && (size_t)group_min < cipher_index) { + cipher_index = group_min; + } + ret = sk_SSL_CIPHER_value(allow, cipher_index); + break; + } + } + + if (in_group_flags != NULL && in_group_flags[i] == 0 && group_min != -1) { + /* We are about to leave a group, but we found a match in it, so that's + * our answer. */ + ret = sk_SSL_CIPHER_value(allow, group_min); + break; + } + } + + sk_SSL_CIPHER_free(clnt); + return ret; +} + +static int ssl3_process_client_hello(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) { + return -1; + } + + SSL_CLIENT_HELLO client_hello; + if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg, + ssl->init_num)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return -1; + } + + /* Run the early callback. */ + if (ssl->ctx->select_certificate_cb != NULL) { + switch (ssl->ctx->select_certificate_cb(&client_hello)) { + case 0: + ssl->rwstate = SSL_CERTIFICATE_SELECTION_PENDING; + return -1; + + case -1: + /* Connection rejected. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + return -1; + + default: + /* fallthrough */; + } + } + + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!negotiate_version(hs, &alert, &client_hello)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; + } + + /* Load the client random. */ + if (client_hello.random_len != SSL3_RANDOM_SIZE) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + OPENSSL_memcpy(ssl->s3->client_random, client_hello.random, + client_hello.random_len); + + /* Only null compression is supported. TLS 1.3 further requires the peer + * advertise no other compression. */ + if (OPENSSL_memchr(client_hello.compression_methods, 0, + client_hello.compression_methods_len) == NULL || + (ssl3_protocol_version(ssl) >= TLS1_3_VERSION && + client_hello.compression_methods_len != 1)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; + } + + /* TLS extensions. */ + if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); + return -1; + } + + return 1; +} + +static int ssl3_select_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + /* Call |cert_cb| to update server certificates if required. */ + if (ssl->cert->cert_cb != NULL) { + int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg); + if (rv == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return -1; + } + if (rv < 0) { + ssl->rwstate = SSL_X509_LOOKUP; + return -1; + } + } + + if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(ssl)) { + return -1; + } + + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + /* Jump to the TLS 1.3 state machine. */ + hs->state = SSL_ST_TLS13; + hs->do_tls13_handshake = tls13_server_handshake; + return 1; + } + + SSL_CLIENT_HELLO client_hello; + if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg, + ssl->init_num)) { + return -1; + } + + /* Negotiate the cipher suite. This must be done after |cert_cb| so the + * certificate is finalized. */ + hs->new_cipher = + ssl3_choose_cipher(hs, &client_hello, ssl_get_cipher_preferences(ssl)); + if (hs->new_cipher == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + return -1; + } + + return 1; +} + +static int ssl3_select_parameters(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + uint8_t al = SSL_AD_INTERNAL_ERROR; + int ret = -1; + SSL_SESSION *session = NULL; + + SSL_CLIENT_HELLO client_hello; + if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg, + ssl->init_num)) { + return -1; + } + + /* Determine whether we are doing session resumption. */ + int tickets_supported = 0, renew_ticket = 0; + switch (ssl_get_prev_session(ssl, &session, &tickets_supported, &renew_ticket, + &client_hello)) { + case ssl_session_success: + break; + case ssl_session_error: + goto err; + case ssl_session_retry: + ssl->rwstate = SSL_PENDING_SESSION; + goto err; + case ssl_session_ticket_retry: + ssl->rwstate = SSL_PENDING_TICKET; + goto err; + } + + if (session != NULL) { + if (session->extended_master_secret && !hs->extended_master_secret) { + /* A ClientHello without EMS that attempts to resume a session with EMS + * is fatal to the connection. */ + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, SSL_R_RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION); + goto f_err; + } + + if (!ssl_session_is_resumable(hs, session) || + /* If the client offers the EMS extension, but the previous session + * didn't use it, then negotiate a new session. */ + hs->extended_master_secret != session->extended_master_secret) { + SSL_SESSION_free(session); + session = NULL; + } + } + + if (session != NULL) { + /* Use the old session. */ + hs->ticket_expected = renew_ticket; + ssl->session = session; + session = NULL; + ssl->s3->session_reused = 1; + } else { + hs->ticket_expected = tickets_supported; + ssl_set_session(ssl, NULL); + if (!ssl_get_new_session(hs, 1 /* server */)) { + goto err; + } + + /* Clear the session ID if we want the session to be single-use. */ + if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) { + hs->new_session->session_id_length = 0; + } + } + + if (ssl->ctx->dos_protection_cb != NULL && + ssl->ctx->dos_protection_cb(&client_hello) == 0) { + /* Connection rejected for DOS reasons. */ + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED); + goto f_err; + } + + if (ssl->session == NULL) { + hs->new_session->cipher = hs->new_cipher; + + /* On new sessions, stash the SNI value in the session. */ + if (hs->hostname != NULL) { + OPENSSL_free(hs->new_session->tlsext_hostname); + hs->new_session->tlsext_hostname = BUF_strdup(hs->hostname); + if (hs->new_session->tlsext_hostname == NULL) { + al = SSL_AD_INTERNAL_ERROR; + goto f_err; + } + } + + /* Determine whether to request a client certificate. */ + hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER); + /* Only request a certificate if Channel ID isn't negotiated. */ + if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) && + ssl->s3->tlsext_channel_id_valid) { + hs->cert_request = 0; + } + /* CertificateRequest may only be sent in certificate-based ciphers. */ + if (!ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + hs->cert_request = 0; + } + + if (!hs->cert_request) { + /* OpenSSL returns X509_V_OK when no certificates are requested. This is + * classed by them as a bug, but it's assumed by at least NGINX. */ + hs->new_session->verify_result = X509_V_OK; + } + } + + /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was + * deferred. Complete it now. */ + if (!ssl_negotiate_alpn(hs, &al, &client_hello)) { + goto f_err; + } + + /* Now that all parameters are known, initialize the handshake hash and hash + * the ClientHello. */ + if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(ssl), + hs->new_cipher->algorithm_prf) || + !ssl_hash_current_message(hs)) { + goto f_err; + } + + /* Release the handshake buffer if client authentication isn't required. */ + if (!hs->cert_request) { + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + } + + ret = 1; + + if (0) { + f_err: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); + } + +err: + SSL_SESSION_free(session); + return ret; +} + +static int ssl3_send_server_hello(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + + /* We only accept ChannelIDs on connections with ECDHE in order to avoid a + * known attack while we fix ChannelID itself. */ + if (ssl->s3->tlsext_channel_id_valid && + (hs->new_cipher->algorithm_mkey & SSL_kECDHE) == 0) { + ssl->s3->tlsext_channel_id_valid = 0; + } + + /* If this is a resumption and the original handshake didn't support + * ChannelID then we didn't record the original handshake hashes in the + * session and so cannot resume with ChannelIDs. */ + if (ssl->session != NULL && + ssl->session->original_handshake_hash_len == 0) { + ssl->s3->tlsext_channel_id_valid = 0; + } + + struct OPENSSL_timeval now; + ssl_get_current_time(ssl, &now); + ssl->s3->server_random[0] = now.tv_sec >> 24; + ssl->s3->server_random[1] = now.tv_sec >> 16; + ssl->s3->server_random[2] = now.tv_sec >> 8; + ssl->s3->server_random[3] = now.tv_sec; + if (!RAND_bytes(ssl->s3->server_random + 4, SSL3_RANDOM_SIZE - 4)) { + return -1; + } + + /* TODO(davidben): Implement the TLS 1.1 and 1.2 downgrade sentinels once TLS + * 1.3 is finalized and we are not implementing a draft version. */ + + const SSL_SESSION *session = hs->new_session; + if (ssl->session != NULL) { + session = ssl->session; + } + + CBB cbb, body, session_id; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) || + !CBB_add_u16(&body, ssl->version) || + !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) || + !CBB_add_u8_length_prefixed(&body, &session_id) || + !CBB_add_bytes(&session_id, session->session_id, + session->session_id_length) || + !CBB_add_u16(&body, ssl_cipher_get_value(hs->new_cipher)) || + !CBB_add_u8(&body, 0 /* no compression */) || + !ssl_add_serverhello_tlsext(hs, &body) || + !ssl_add_message_cbb(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + return 1; +} + +static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_has_certificate(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET); + return -1; + } + + if (!ssl3_output_cert_chain(ssl)) { + return -1; + } + return 1; +} + +static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + CBB cbb, body, ocsp_response; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CERTIFICATE_STATUS) || + !CBB_add_u8(&body, TLSEXT_STATUSTYPE_ocsp) || + !CBB_add_u24_length_prefixed(&body, &ocsp_response) || + !CBB_add_bytes(&ocsp_response, + CRYPTO_BUFFER_data(ssl->cert->ocsp_response), + CRYPTO_BUFFER_len(ssl->cert->ocsp_response)) || + !ssl_add_message_cbb(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + return 1; +} + +static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + CBB cbb, child; + CBB_zero(&cbb); + + /* Put together the parameters. */ + if (hs->state == SSL3_ST_SW_KEY_EXCH_A) { + uint32_t alg_k = hs->new_cipher->algorithm_mkey; + uint32_t alg_a = hs->new_cipher->algorithm_auth; + + /* Pre-allocate enough room to comfortably fit an ECDHE public key. */ + if (!CBB_init(&cbb, 128)) { + goto err; + } + + /* PSK ciphers begin with an identity hint. */ + if (alg_a & SSL_aPSK) { + size_t len = + (ssl->psk_identity_hint == NULL) ? 0 : strlen(ssl->psk_identity_hint); + if (!CBB_add_u16_length_prefixed(&cbb, &child) || + !CBB_add_bytes(&child, (const uint8_t *)ssl->psk_identity_hint, + len)) { + goto err; + } + } + + if (alg_k & SSL_kDHE) { + /* Determine the group to use. */ + DH *params = ssl->cert->dh_tmp; + if (params == NULL && ssl->cert->dh_tmp_cb != NULL) { + params = ssl->cert->dh_tmp_cb(ssl, 0, 1024); + } + if (params == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_DH_KEY); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + goto err; + } + + /* Set up DH, generate a key, and emit the public half. */ + DH *dh = DHparams_dup(params); + if (dh == NULL) { + goto err; + } + + SSL_ECDH_CTX_init_for_dhe(&hs->ecdh_ctx, dh); + if (!CBB_add_u16_length_prefixed(&cbb, &child) || + !BN_bn2cbb_padded(&child, BN_num_bytes(params->p), params->p) || + !CBB_add_u16_length_prefixed(&cbb, &child) || + !BN_bn2cbb_padded(&child, BN_num_bytes(params->g), params->g) || + !CBB_add_u16_length_prefixed(&cbb, &child) || + !SSL_ECDH_CTX_offer(&hs->ecdh_ctx, &child)) { + goto err; + } + } else if (alg_k & SSL_kECDHE) { + /* Determine the group to use. */ + uint16_t group_id; + if (!tls1_get_shared_group(hs, &group_id)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_ECDH_KEY); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + goto err; + } + hs->new_session->group_id = group_id; + + /* Set up ECDH, generate a key, and emit the public half. */ + if (!SSL_ECDH_CTX_init(&hs->ecdh_ctx, group_id) || + !CBB_add_u8(&cbb, NAMED_CURVE_TYPE) || + !CBB_add_u16(&cbb, group_id) || + !CBB_add_u8_length_prefixed(&cbb, &child) || + !SSL_ECDH_CTX_offer(&hs->ecdh_ctx, &child)) { + goto err; + } + } else { + assert(alg_k & SSL_kPSK); + } + + if (!CBB_finish(&cbb, &hs->server_params, &hs->server_params_len)) { + goto err; + } + } + + /* Assemble the message. */ + CBB body; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_SERVER_KEY_EXCHANGE) || + !CBB_add_bytes(&body, hs->server_params, hs->server_params_len)) { + goto err; + } + + /* Add a signature. */ + if (ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + if (!ssl_has_private_key(ssl)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + + /* Determine the signature algorithm. */ + uint16_t signature_algorithm; + if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) { + goto err; + } + if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { + if (!CBB_add_u16(&body, signature_algorithm)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + } + + /* Add space for the signature. */ + const size_t max_sig_len = ssl_private_key_max_signature_len(ssl); + uint8_t *ptr; + if (!CBB_add_u16_length_prefixed(&body, &child) || + !CBB_reserve(&child, &ptr, max_sig_len)) { + goto err; + } + + size_t sig_len; + enum ssl_private_key_result_t sign_result; + if (hs->state == SSL3_ST_SW_KEY_EXCH_A) { + CBB transcript; + uint8_t *transcript_data; + size_t transcript_len; + if (!CBB_init(&transcript, + 2 * SSL3_RANDOM_SIZE + hs->server_params_len) || + !CBB_add_bytes(&transcript, ssl->s3->client_random, + SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&transcript, ssl->s3->server_random, + SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&transcript, hs->server_params, + hs->server_params_len) || + !CBB_finish(&transcript, &transcript_data, &transcript_len)) { + CBB_cleanup(&transcript); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + + sign_result = ssl_private_key_sign(ssl, ptr, &sig_len, max_sig_len, + signature_algorithm, transcript_data, + transcript_len); + OPENSSL_free(transcript_data); + } else { + assert(hs->state == SSL3_ST_SW_KEY_EXCH_B); + sign_result = ssl_private_key_complete(ssl, ptr, &sig_len, max_sig_len); + } + + switch (sign_result) { + case ssl_private_key_success: + if (!CBB_did_write(&child, sig_len)) { + goto err; + } + break; + case ssl_private_key_failure: + goto err; + case ssl_private_key_retry: + ssl->rwstate = SSL_PRIVATE_KEY_OPERATION; + hs->state = SSL3_ST_SW_KEY_EXCH_B; + goto err; + } + } + + if (!ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + OPENSSL_free(hs->server_params); + hs->server_params = NULL; + hs->server_params_len = 0; + + return 1; + +err: + CBB_cleanup(&cbb); + return -1; +} + +static int add_cert_types(SSL *ssl, CBB *cbb) { + /* Get configured signature algorithms. */ + int have_rsa_sign = 0; + int have_ecdsa_sign = 0; + const uint16_t *sig_algs; + size_t num_sig_algs = tls12_get_verify_sigalgs(ssl, &sig_algs); + for (size_t i = 0; i < num_sig_algs; i++) { + switch (sig_algs[i]) { + case SSL_SIGN_RSA_PKCS1_SHA512: + case SSL_SIGN_RSA_PKCS1_SHA384: + case SSL_SIGN_RSA_PKCS1_SHA256: + case SSL_SIGN_RSA_PKCS1_SHA1: + have_rsa_sign = 1; + break; + + case SSL_SIGN_ECDSA_SECP521R1_SHA512: + case SSL_SIGN_ECDSA_SECP384R1_SHA384: + case SSL_SIGN_ECDSA_SECP256R1_SHA256: + case SSL_SIGN_ECDSA_SHA1: + have_ecdsa_sign = 1; + break; + } + } + + if (have_rsa_sign && !CBB_add_u8(cbb, SSL3_CT_RSA_SIGN)) { + return 0; + } + + /* ECDSA certs can be used with RSA cipher suites as well so we don't need to + * check for SSL_kECDH or SSL_kECDHE. */ + if (ssl->version >= TLS1_VERSION && have_ecdsa_sign && + !CBB_add_u8(cbb, TLS_CT_ECDSA_SIGN)) { + return 0; + } + + return 1; +} + +static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + CBB cbb, body, cert_types, sigalgs_cbb; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CERTIFICATE_REQUEST) || + !CBB_add_u8_length_prefixed(&body, &cert_types) || + !add_cert_types(ssl, &cert_types)) { + goto err; + } + + if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { + const uint16_t *sigalgs; + size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs); + if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) { + goto err; + } + + for (size_t i = 0; i < num_sigalgs; i++) { + if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) { + goto err; + } + } + } + + if (!ssl_add_client_CA_list(ssl, &body) || + !ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + return 1; + +err: + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; +} + +static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) || + !ssl_add_message_cbb(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + return 1; +} + +static int ssl3_get_client_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + assert(hs->cert_request); + + int msg_ret = ssl->method->ssl_get_message(ssl); + if (msg_ret <= 0) { + return msg_ret; + } + + if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE) { + if (ssl->version == SSL3_VERSION && + ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) { + /* In SSL 3.0, the Certificate message is omitted to signal no + * certificate. */ + if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + return -1; + } + + /* OpenSSL returns X509_V_OK when no certificates are received. This is + * classed by them as a bug, but it's assumed by at least NGINX. */ + hs->new_session->verify_result = X509_V_OK; + ssl->s3->tmp.reuse_message = 1; + return 1; + } + + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return -1; + } + + if (!ssl_hash_current_message(hs)) { + return -1; + } + + CBS certificate_msg; + CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num); + + sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free); + EVP_PKEY_free(hs->peer_pubkey); + hs->peer_pubkey = NULL; + uint8_t alert = SSL_AD_DECODE_ERROR; + hs->new_session->certs = ssl_parse_cert_chain( + &alert, &hs->peer_pubkey, + ssl->retain_only_sha256_of_client_certs ? hs->new_session->peer_sha256 + : NULL, + &certificate_msg, ssl->ctx->pool); + if (hs->new_session->certs == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; + } + + if (CBS_len(&certificate_msg) != 0 || + !ssl->ctx->x509_method->session_cache_objects(hs->new_session)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return -1; + } + + if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) { + /* No client certificate so the handshake buffer may be discarded. */ + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + + /* In SSL 3.0, sending no certificate is signaled by omitting the + * Certificate message. */ + if (ssl->version == SSL3_VERSION) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + return -1; + } + + if (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) { + /* Fail for TLS only if we required a certificate */ + OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + return -1; + } + + /* OpenSSL returns X509_V_OK when no certificates are received. This is + * classed by them as a bug, but it's assumed by at least NGINX. */ + hs->new_session->verify_result = X509_V_OK; + return 1; + } + + /* The hash will have been filled in. */ + if (ssl->retain_only_sha256_of_client_certs) { + hs->new_session->peer_sha256_valid = 1; + } + + if (!ssl->ctx->x509_method->session_verify_cert_chain(hs->new_session, ssl)) { + return -1; + } + + return 1; +} + +static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int al; + CBS client_key_exchange; + uint32_t alg_k; + uint32_t alg_a; + uint8_t *premaster_secret = NULL; + size_t premaster_secret_len = 0; + uint8_t *decrypt_buf = NULL; + + unsigned psk_len = 0; + uint8_t psk[PSK_MAX_PSK_LEN]; + + if (hs->state == SSL3_ST_SR_KEY_EXCH_A) { + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE) || + !ssl_hash_current_message(hs)) { + return -1; + } + } + + CBS_init(&client_key_exchange, ssl->init_msg, ssl->init_num); + alg_k = hs->new_cipher->algorithm_mkey; + alg_a = hs->new_cipher->algorithm_auth; + + /* If using a PSK key exchange, prepare the pre-shared key. */ + if (alg_a & SSL_aPSK) { + CBS psk_identity; + + /* If using PSK, the ClientKeyExchange contains a psk_identity. If PSK, + * then this is the only field in the message. */ + if (!CBS_get_u16_length_prefixed(&client_key_exchange, &psk_identity) || + ((alg_k & SSL_kPSK) && CBS_len(&client_key_exchange) != 0)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + al = SSL_AD_DECODE_ERROR; + goto f_err; + } + + if (ssl->psk_server_callback == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_SERVER_CB); + al = SSL_AD_INTERNAL_ERROR; + goto f_err; + } + + if (CBS_len(&psk_identity) > PSK_MAX_IDENTITY_LEN || + CBS_contains_zero_byte(&psk_identity)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + al = SSL_AD_ILLEGAL_PARAMETER; + goto f_err; + } + + if (!CBS_strdup(&psk_identity, &hs->new_session->psk_identity)) { + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto f_err; + } + + /* Look up the key for the identity. */ + psk_len = ssl->psk_server_callback(ssl, hs->new_session->psk_identity, psk, + sizeof(psk)); + if (psk_len > PSK_MAX_PSK_LEN) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + al = SSL_AD_INTERNAL_ERROR; + goto f_err; + } else if (psk_len == 0) { + /* PSK related to the given identity not found */ + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND); + al = SSL_AD_UNKNOWN_PSK_IDENTITY; + goto f_err; + } + } + + /* Depending on the key exchange method, compute |premaster_secret| and + * |premaster_secret_len|. */ + if (alg_k & SSL_kRSA) { + /* Allocate a buffer large enough for an RSA decryption. */ + const size_t rsa_size = ssl_private_key_max_signature_len(ssl); + decrypt_buf = OPENSSL_malloc(rsa_size); + if (decrypt_buf == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + enum ssl_private_key_result_t decrypt_result; + size_t decrypt_len; + if (hs->state == SSL3_ST_SR_KEY_EXCH_A) { + if (!ssl_has_private_key(ssl) || + ssl_private_key_type(ssl) != NID_rsaEncryption) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_RSA_CERTIFICATE); + goto f_err; + } + CBS encrypted_premaster_secret; + if (ssl->version > SSL3_VERSION) { + if (!CBS_get_u16_length_prefixed(&client_key_exchange, + &encrypted_premaster_secret) || + CBS_len(&client_key_exchange) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, + SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG); + goto f_err; + } + } else { + encrypted_premaster_secret = client_key_exchange; + } + + /* Decrypt with no padding. PKCS#1 padding will be removed as part of the + * timing-sensitive code below. */ + decrypt_result = ssl_private_key_decrypt( + ssl, decrypt_buf, &decrypt_len, rsa_size, + CBS_data(&encrypted_premaster_secret), + CBS_len(&encrypted_premaster_secret)); + } else { + assert(hs->state == SSL3_ST_SR_KEY_EXCH_B); + /* Complete async decrypt. */ + decrypt_result = + ssl_private_key_complete(ssl, decrypt_buf, &decrypt_len, rsa_size); + } + + switch (decrypt_result) { + case ssl_private_key_success: + break; + case ssl_private_key_failure: + goto err; + case ssl_private_key_retry: + ssl->rwstate = SSL_PRIVATE_KEY_OPERATION; + hs->state = SSL3_ST_SR_KEY_EXCH_B; + goto err; + } + + if (decrypt_len != rsa_size) { + al = SSL_AD_DECRYPT_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED); + goto f_err; + } + + /* Prepare a random premaster, to be used on invalid padding. See RFC 5246, + * section 7.4.7.1. */ + premaster_secret_len = SSL_MAX_MASTER_KEY_LENGTH; + premaster_secret = OPENSSL_malloc(premaster_secret_len); + if (premaster_secret == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + if (!RAND_bytes(premaster_secret, premaster_secret_len)) { + goto err; + } + + /* The smallest padded premaster is 11 bytes of overhead. Small keys are + * publicly invalid. */ + if (decrypt_len < 11 + premaster_secret_len) { + al = SSL_AD_DECRYPT_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED); + goto f_err; + } + + /* Check the padding. See RFC 3447, section 7.2.2. */ + size_t padding_len = decrypt_len - premaster_secret_len; + uint8_t good = constant_time_eq_int_8(decrypt_buf[0], 0) & + constant_time_eq_int_8(decrypt_buf[1], 2); + for (size_t i = 2; i < padding_len - 1; i++) { + good &= ~constant_time_is_zero_8(decrypt_buf[i]); + } + good &= constant_time_is_zero_8(decrypt_buf[padding_len - 1]); + + /* The premaster secret must begin with |client_version|. This too must be + * checked in constant time (http://eprint.iacr.org/2003/052/). */ + good &= constant_time_eq_8(decrypt_buf[padding_len], + (unsigned)(hs->client_version >> 8)); + good &= constant_time_eq_8(decrypt_buf[padding_len + 1], + (unsigned)(hs->client_version & 0xff)); + + /* Select, in constant time, either the decrypted premaster or the random + * premaster based on |good|. */ + for (size_t i = 0; i < premaster_secret_len; i++) { + premaster_secret[i] = constant_time_select_8( + good, decrypt_buf[padding_len + i], premaster_secret[i]); + } + + OPENSSL_free(decrypt_buf); + decrypt_buf = NULL; + } else if (alg_k & (SSL_kECDHE|SSL_kDHE)) { + /* Parse the ClientKeyExchange. */ + CBS peer_key; + if (!SSL_ECDH_CTX_get_key(&hs->ecdh_ctx, &client_key_exchange, &peer_key) || + CBS_len(&client_key_exchange) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + /* Compute the premaster. */ + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!SSL_ECDH_CTX_finish(&hs->ecdh_ctx, &premaster_secret, + &premaster_secret_len, &alert, CBS_data(&peer_key), + CBS_len(&peer_key))) { + al = alert; + goto f_err; + } + + /* The key exchange state may now be discarded. */ + SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx); + } else if (alg_k & SSL_kPSK) { + /* For plain PSK, other_secret is a block of 0s with the same length as the + * pre-shared key. */ + premaster_secret_len = psk_len; + premaster_secret = OPENSSL_malloc(premaster_secret_len); + if (premaster_secret == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + OPENSSL_memset(premaster_secret, 0, premaster_secret_len); + } else { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_TYPE); + goto f_err; + } + + /* For a PSK cipher suite, the actual pre-master secret is combined with the + * pre-shared key. */ + if (alg_a & SSL_aPSK) { + CBB new_premaster, child; + uint8_t *new_data; + size_t new_len; + + CBB_zero(&new_premaster); + if (!CBB_init(&new_premaster, 2 + psk_len + 2 + premaster_secret_len) || + !CBB_add_u16_length_prefixed(&new_premaster, &child) || + !CBB_add_bytes(&child, premaster_secret, premaster_secret_len) || + !CBB_add_u16_length_prefixed(&new_premaster, &child) || + !CBB_add_bytes(&child, psk, psk_len) || + !CBB_finish(&new_premaster, &new_data, &new_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + CBB_cleanup(&new_premaster); + goto err; + } + + OPENSSL_cleanse(premaster_secret, premaster_secret_len); + OPENSSL_free(premaster_secret); + premaster_secret = new_data; + premaster_secret_len = new_len; + } + + /* Compute the master secret */ + hs->new_session->master_key_length = tls1_generate_master_secret( + hs, hs->new_session->master_key, premaster_secret, premaster_secret_len); + if (hs->new_session->master_key_length == 0) { + goto err; + } + hs->new_session->extended_master_secret = hs->extended_master_secret; + + OPENSSL_cleanse(premaster_secret, premaster_secret_len); + OPENSSL_free(premaster_secret); + return 1; + +f_err: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); +err: + if (premaster_secret != NULL) { + OPENSSL_cleanse(premaster_secret, premaster_secret_len); + OPENSSL_free(premaster_secret); + } + OPENSSL_free(decrypt_buf); + + return -1; +} + +static int ssl3_get_cert_verify(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int al; + CBS certificate_verify, signature; + + /* Only RSA and ECDSA client certificates are supported, so a + * CertificateVerify is required if and only if there's a client certificate. + * */ + if (hs->peer_pubkey == NULL) { + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + return 1; + } + + int msg_ret = ssl->method->ssl_get_message(ssl); + if (msg_ret <= 0) { + return msg_ret; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY)) { + return -1; + } + + CBS_init(&certificate_verify, ssl->init_msg, ssl->init_num); + + /* Determine the digest type if needbe. */ + uint16_t signature_algorithm = 0; + if (ssl3_protocol_version(ssl) >= TLS1_2_VERSION) { + if (!CBS_get_u16(&certificate_verify, &signature_algorithm)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + if (!tls12_check_peer_sigalg(ssl, &al, signature_algorithm)) { + goto f_err; + } + hs->new_session->peer_signature_algorithm = signature_algorithm; + } else if (hs->peer_pubkey->type == EVP_PKEY_RSA) { + signature_algorithm = SSL_SIGN_RSA_PKCS1_MD5_SHA1; + } else if (hs->peer_pubkey->type == EVP_PKEY_EC) { + signature_algorithm = SSL_SIGN_ECDSA_SHA1; + } else { + al = SSL_AD_UNSUPPORTED_CERTIFICATE; + OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE); + goto f_err; + } + + /* Parse and verify the signature. */ + if (!CBS_get_u16_length_prefixed(&certificate_verify, &signature) || + CBS_len(&certificate_verify) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto f_err; + } + + int sig_ok; + /* The SSL3 construction for CertificateVerify does not decompose into a + * single final digest and signature, and must be special-cased. */ + if (ssl3_protocol_version(ssl) == SSL3_VERSION) { + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_len; + if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(&hs->transcript, digest, + &digest_len, hs->new_session, + signature_algorithm)) { + goto err; + } + + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(hs->peer_pubkey, NULL); + sig_ok = pctx != NULL && + EVP_PKEY_verify_init(pctx) && + EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature), + digest, digest_len); + EVP_PKEY_CTX_free(pctx); + } else { + sig_ok = ssl_public_key_verify( + ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm, + hs->peer_pubkey, (const uint8_t *)hs->transcript.buffer->data, + hs->transcript.buffer->length); + } + +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + sig_ok = 1; + ERR_clear_error(); +#endif + if (!sig_ok) { + al = SSL_AD_DECRYPT_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE); + goto f_err; + } + + /* The handshake buffer is no longer necessary, and we may hash the current + * message.*/ + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + if (!ssl_hash_current_message(hs)) { + goto err; + } + + return 1; + +f_err: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); +err: + return 0; +} + +/* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It + * sets the next_proto member in s if found */ +static int ssl3_get_next_proto(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_NEXT_PROTO) || + !ssl_hash_current_message(hs)) { + return -1; + } + + CBS next_protocol, selected_protocol, padding; + CBS_init(&next_protocol, ssl->init_msg, ssl->init_num); + if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) || + !CBS_get_u8_length_prefixed(&next_protocol, &padding) || + CBS_len(&next_protocol) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return 0; + } + + if (!CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated, + &ssl->s3->next_proto_negotiated_len)) { + return 0; + } + + return 1; +} + +/* ssl3_get_channel_id reads and verifies a ClientID handshake message. */ +static int ssl3_get_channel_id(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int msg_ret = ssl->method->ssl_get_message(ssl); + if (msg_ret <= 0) { + return msg_ret; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_CHANNEL_ID) || + !tls1_verify_channel_id(hs) || + !ssl_hash_current_message(hs)) { + return -1; + } + return 1; +} + +static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + const SSL_SESSION *session; + SSL_SESSION *session_copy = NULL; + if (ssl->session == NULL) { + /* Fix the timeout to measure from the ticket issuance time. */ + ssl_session_rebase_time(ssl, hs->new_session); + session = hs->new_session; + } else { + /* We are renewing an existing session. Duplicate the session to adjust the + * timeout. */ + session_copy = SSL_SESSION_dup(ssl->session, SSL_SESSION_INCLUDE_NONAUTH); + if (session_copy == NULL) { + return -1; + } + + ssl_session_rebase_time(ssl, session_copy); + session = session_copy; + } + + CBB cbb, body, ticket; + int ok = + ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_NEW_SESSION_TICKET) && + CBB_add_u32(&body, session->timeout) && + CBB_add_u16_length_prefixed(&body, &ticket) && + ssl_encrypt_ticket(ssl, &ticket, session) && + ssl_add_message_cbb(ssl, &cbb); + + SSL_SESSION_free(session_copy); + CBB_cleanup(&cbb); + + if (!ok) { + return -1; + } + + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/internal.h b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/internal.h new file mode 100644 index 000000000..99980d843 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/internal.h @@ -0,0 +1,2231 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ + +#ifndef OPENSSL_HEADER_SSL_INTERNAL_H +#define OPENSSL_HEADER_SSL_INTERNAL_H + +#include <openssl/base.h> + +#include <openssl/aead.h> +#include <openssl/ssl.h> +#include <openssl/stack.h> + + +#if defined(OPENSSL_WINDOWS) +/* Windows defines struct timeval in winsock2.h. */ +OPENSSL_MSVC_PRAGMA(warning(push, 3)) +#include <winsock2.h> +OPENSSL_MSVC_PRAGMA(warning(pop)) +#else +#include <sys/time.h> +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + + +/* Cipher suites. */ + +/* Bits for |algorithm_mkey| (key exchange algorithm). */ +#define SSL_kRSA 0x00000001L +#define SSL_kDHE 0x00000002L +#define SSL_kECDHE 0x00000004L +/* SSL_kPSK is only set for plain PSK, not ECDHE_PSK. */ +#define SSL_kPSK 0x00000008L +#define SSL_kGENERIC 0x00000010L + +/* Bits for |algorithm_auth| (server authentication). */ +#define SSL_aRSA 0x00000001L +#define SSL_aECDSA 0x00000002L +/* SSL_aPSK is set for both PSK and ECDHE_PSK. */ +#define SSL_aPSK 0x00000004L +#define SSL_aGENERIC 0x00000008L + +#define SSL_aCERT (SSL_aRSA | SSL_aECDSA) + +/* Bits for |algorithm_enc| (symmetric encryption). */ +#define SSL_3DES 0x00000001L +#define SSL_AES128 0x00000002L +#define SSL_AES256 0x00000004L +#define SSL_AES128GCM 0x00000008L +#define SSL_AES256GCM 0x00000010L +#define SSL_eNULL 0x00000020L +#define SSL_CHACHA20POLY1305 0x00000040L + +#define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM) + +/* Bits for |algorithm_mac| (symmetric authentication). */ +#define SSL_SHA1 0x00000001L +#define SSL_SHA256 0x00000002L +#define SSL_SHA384 0x00000004L +/* SSL_AEAD is set for all AEADs. */ +#define SSL_AEAD 0x00000008L + +/* Bits for |algorithm_prf| (handshake digest). */ +#define SSL_HANDSHAKE_MAC_DEFAULT 0x1 +#define SSL_HANDSHAKE_MAC_SHA256 0x2 +#define SSL_HANDSHAKE_MAC_SHA384 0x4 + +/* SSL_MAX_DIGEST is the number of digest types which exist. When adding a new + * one, update the table in ssl_cipher.c. */ +#define SSL_MAX_DIGEST 4 + +/* ssl_cipher_get_evp_aead sets |*out_aead| to point to the correct EVP_AEAD + * object for |cipher| protocol version |version|. It sets |*out_mac_secret_len| + * and |*out_fixed_iv_len| to the MAC key length and fixed IV length, + * respectively. The MAC key length is zero except for legacy block and stream + * ciphers. It returns 1 on success and 0 on error. */ +int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead, + size_t *out_mac_secret_len, + size_t *out_fixed_iv_len, + const SSL_CIPHER *cipher, uint16_t version); + +/* ssl_get_handshake_digest returns the |EVP_MD| corresponding to + * |algorithm_prf| and the |version|. */ +const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf, + uint16_t version); + +/* ssl_create_cipher_list evaluates |rule_str| according to the ciphers in + * |ssl_method|. It sets |*out_cipher_list| to a newly-allocated + * |ssl_cipher_preference_list_st| containing the result. It returns 1 on + * success and 0 on failure. If |strict| is true, nonsense will be rejected. If + * false, nonsense will be silently ignored. An empty result is considered an + * error regardless of |strict|. */ +int ssl_create_cipher_list( + const SSL_PROTOCOL_METHOD *ssl_method, + struct ssl_cipher_preference_list_st **out_cipher_list, + const char *rule_str, int strict); + +/* ssl_cipher_get_value returns the cipher suite id of |cipher|. */ +uint16_t ssl_cipher_get_value(const SSL_CIPHER *cipher); + +/* ssl_cipher_get_key_type returns the |EVP_PKEY_*| value corresponding to the + * server key used in |cipher| or |EVP_PKEY_NONE| if there is none. */ +int ssl_cipher_get_key_type(const SSL_CIPHER *cipher); + +/* ssl_cipher_uses_certificate_auth returns one if |cipher| authenticates the + * server and, optionally, the client with a certificate. Otherwise it returns + * zero. */ +int ssl_cipher_uses_certificate_auth(const SSL_CIPHER *cipher); + +/* ssl_cipher_requires_server_key_exchange returns 1 if |cipher| requires a + * ServerKeyExchange message. Otherwise it returns 0. + * + * This function may return zero while still allowing |cipher| an optional + * ServerKeyExchange. This is the case for plain PSK ciphers. */ +int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher); + +/* ssl_cipher_get_record_split_len, for TLS 1.0 CBC mode ciphers, returns the + * length of an encrypted 1-byte record, for use in record-splitting. Otherwise + * it returns zero. */ +size_t ssl_cipher_get_record_split_len(const SSL_CIPHER *cipher); + + +/* Transcript layer. */ + +/* SSL_TRANSCRIPT maintains the handshake transcript as a combination of a + * buffer and running hash. */ +typedef struct ssl_transcript_st { + /* buffer, if non-NULL, contains the handshake transcript. */ + BUF_MEM *buffer; + /* hash, if initialized with an |EVP_MD|, maintains the handshake hash. For + * TLS 1.1 and below, it is the SHA-1 half. */ + EVP_MD_CTX hash; + /* md5, if initialized with an |EVP_MD|, maintains the MD5 half of the + * handshake hash for TLS 1.1 and below. */ + EVP_MD_CTX md5; +} SSL_TRANSCRIPT; + +/* SSL_TRANSCRIPT_init initializes the handshake transcript. If called on an + * existing transcript, it resets the transcript and hash. It returns one on + * success and zero on failure. */ +int SSL_TRANSCRIPT_init(SSL_TRANSCRIPT *transcript); + +/* SSL_TRANSCRIPT_init_hash initializes the handshake hash based on the PRF and + * contents of the handshake transcript. Subsequent calls to + * |SSL_TRANSCRIPT_update| will update the rolling hash. It returns one on + * success and zero on failure. It is an error to call this function after the + * handshake buffer is released. */ +int SSL_TRANSCRIPT_init_hash(SSL_TRANSCRIPT *transcript, uint16_t version, + int algorithm_prf); + +/* SSL_TRANSCRIPT_cleanup cleans up the hash and transcript. */ +void SSL_TRANSCRIPT_cleanup(SSL_TRANSCRIPT *transcript); + +/* SSL_TRANSCRIPT_free_buffer releases the handshake buffer. Subsequent calls to + * |SSL_TRANSCRIPT_update| will not update the handshake buffer. */ +void SSL_TRANSCRIPT_free_buffer(SSL_TRANSCRIPT *transcript); + +/* SSL_TRANSCRIPT_digest_len returns the length of the PRF hash. */ +size_t SSL_TRANSCRIPT_digest_len(const SSL_TRANSCRIPT *transcript); + +/* SSL_TRANSCRIPT_md returns the PRF hash. For TLS 1.1 and below, this is + * |EVP_md5_sha1|. */ +const EVP_MD *SSL_TRANSCRIPT_md(const SSL_TRANSCRIPT *transcript); + +/* SSL_TRANSCRIPT_update adds |in| to the handshake buffer and handshake hash, + * whichever is enabled. It returns one on success and zero on failure. */ +int SSL_TRANSCRIPT_update(SSL_TRANSCRIPT *transcript, const uint8_t *in, + size_t in_len); + +/* SSL_TRANSCRIPT_get_hash writes the handshake hash to |out| which must have + * room for at least |SSL_TRANSCRIPT_digest_len| bytes. On success, it returns + * one and sets |*out_len| to the number of bytes written. Otherwise, it returns + * zero. */ +int SSL_TRANSCRIPT_get_hash(const SSL_TRANSCRIPT *transcript, uint8_t *out, + size_t *out_len); + +/* SSL_TRANSCRIPT_ssl3_cert_verify_hash writes the SSL 3.0 CertificateVerify + * hash into the bytes pointed to by |out| and writes the number of bytes to + * |*out_len|. |out| must have room for |EVP_MAX_MD_SIZE| bytes. It returns one + * on success and zero on failure. */ +int SSL_TRANSCRIPT_ssl3_cert_verify_hash(SSL_TRANSCRIPT *transcript, + uint8_t *out, size_t *out_len, + const SSL_SESSION *session, + int signature_algorithm); + +/* SSL_TRANSCRIPT_finish_mac computes the MAC for the Finished message into the + * bytes pointed by |out| and writes the number of bytes to |*out_len|. |out| + * must have room for |EVP_MAX_MD_SIZE| bytes. It returns one on success and + * zero on failure. */ +int SSL_TRANSCRIPT_finish_mac(SSL_TRANSCRIPT *transcript, uint8_t *out, + size_t *out_len, const SSL_SESSION *session, + int from_server, uint16_t version); + +/* tls1_prf computes the PRF function for |ssl|. It writes |out_len| bytes to + * |out|, using |secret| as the secret and |label| as the label. |seed1| and + * |seed2| are concatenated to form the seed parameter. It returns one on + * success and zero on failure. */ +int tls1_prf(const EVP_MD *digest, uint8_t *out, size_t out_len, + const uint8_t *secret, size_t secret_len, const char *label, + size_t label_len, const uint8_t *seed1, size_t seed1_len, + const uint8_t *seed2, size_t seed2_len); + + +/* Encryption layer. */ + +/* SSL_AEAD_CTX contains information about an AEAD that is being used to encrypt + * an SSL connection. */ +typedef struct ssl_aead_ctx_st { + const SSL_CIPHER *cipher; + EVP_AEAD_CTX ctx; + /* fixed_nonce contains any bytes of the nonce that are fixed for all + * records. */ + uint8_t fixed_nonce[12]; + uint8_t fixed_nonce_len, variable_nonce_len; + /* version is the protocol version that should be used with this AEAD. */ + uint16_t version; + /* variable_nonce_included_in_record is non-zero if the variable nonce + * for a record is included as a prefix before the ciphertext. */ + unsigned variable_nonce_included_in_record : 1; + /* random_variable_nonce is non-zero if the variable nonce is + * randomly generated, rather than derived from the sequence + * number. */ + unsigned random_variable_nonce : 1; + /* omit_length_in_ad is non-zero if the length should be omitted in the + * AEAD's ad parameter. */ + unsigned omit_length_in_ad : 1; + /* omit_version_in_ad is non-zero if the version should be omitted + * in the AEAD's ad parameter. */ + unsigned omit_version_in_ad : 1; + /* omit_ad is non-zero if the AEAD's ad parameter should be omitted. */ + unsigned omit_ad : 1; + /* xor_fixed_nonce is non-zero if the fixed nonce should be XOR'd into the + * variable nonce rather than prepended. */ + unsigned xor_fixed_nonce : 1; +} SSL_AEAD_CTX; + +/* SSL_AEAD_CTX_new creates a newly-allocated |SSL_AEAD_CTX| using the supplied + * key material. It returns NULL on error. Only one of |SSL_AEAD_CTX_open| or + * |SSL_AEAD_CTX_seal| may be used with the resulting object, depending on + * |direction|. |version| is the normalized protocol version, so DTLS 1.0 is + * represented as 0x0301, not 0xffef. */ +SSL_AEAD_CTX *SSL_AEAD_CTX_new(enum evp_aead_direction_t direction, + uint16_t version, const SSL_CIPHER *cipher, + const uint8_t *enc_key, size_t enc_key_len, + const uint8_t *mac_key, size_t mac_key_len, + const uint8_t *fixed_iv, size_t fixed_iv_len); + +/* SSL_AEAD_CTX_free frees |ctx|. */ +void SSL_AEAD_CTX_free(SSL_AEAD_CTX *ctx); + +/* SSL_AEAD_CTX_explicit_nonce_len returns the length of the explicit nonce for + * |ctx|, if any. |ctx| may be NULL to denote the null cipher. */ +size_t SSL_AEAD_CTX_explicit_nonce_len(const SSL_AEAD_CTX *ctx); + +/* SSL_AEAD_CTX_max_overhead returns the maximum overhead of calling + * |SSL_AEAD_CTX_seal|. |ctx| may be NULL to denote the null cipher. */ +size_t SSL_AEAD_CTX_max_overhead(const SSL_AEAD_CTX *ctx); + +/* SSL_AEAD_CTX_open authenticates and decrypts |in_len| bytes from |in| + * in-place. On success, it sets |*out| to the plaintext in |in| and returns + * one. Otherwise, it returns zero. |ctx| may be NULL to denote the null cipher. + * The output will always be |explicit_nonce_len| bytes ahead of |in|. */ +int SSL_AEAD_CTX_open(SSL_AEAD_CTX *ctx, CBS *out, uint8_t type, + uint16_t wire_version, const uint8_t seqnum[8], + uint8_t *in, size_t in_len); + +/* SSL_AEAD_CTX_seal encrypts and authenticates |in_len| bytes from |in| and + * writes the result to |out|. It returns one on success and zero on + * error. |ctx| may be NULL to denote the null cipher. + * + * If |in| and |out| alias then |out| + |explicit_nonce_len| must be == |in|. */ +int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, + size_t max_out, uint8_t type, uint16_t wire_version, + const uint8_t seqnum[8], const uint8_t *in, + size_t in_len); + + +/* DTLS replay bitmap. */ + +/* DTLS1_BITMAP maintains a sliding window of 64 sequence numbers to detect + * replayed packets. It should be initialized by zeroing every field. */ +typedef struct dtls1_bitmap_st { + /* map is a bit mask of the last 64 sequence numbers. Bit + * |1<<i| corresponds to |max_seq_num - i|. */ + uint64_t map; + /* max_seq_num is the largest sequence number seen so far as a 64-bit + * integer. */ + uint64_t max_seq_num; +} DTLS1_BITMAP; + + +/* Record layer. */ + +/* ssl_record_sequence_update increments the sequence number in |seq|. It + * returns one on success and zero on wraparound. */ +int ssl_record_sequence_update(uint8_t *seq, size_t seq_len); + +/* ssl_record_prefix_len returns the length of the prefix before the ciphertext + * of a record for |ssl|. + * + * TODO(davidben): Expose this as part of public API once the high-level + * buffer-free APIs are available. */ +size_t ssl_record_prefix_len(const SSL *ssl); + +enum ssl_open_record_t { + ssl_open_record_success, + ssl_open_record_discard, + ssl_open_record_partial, + ssl_open_record_close_notify, + ssl_open_record_fatal_alert, + ssl_open_record_error, +}; + +/* tls_open_record decrypts a record from |in| in-place. + * + * If the input did not contain a complete record, it returns + * |ssl_open_record_partial|. It sets |*out_consumed| to the total number of + * bytes necessary. It is guaranteed that a successful call to |tls_open_record| + * will consume at least that many bytes. + * + * Otherwise, it sets |*out_consumed| to the number of bytes of input + * consumed. Note that input may be consumed on all return codes if a record was + * decrypted. + * + * On success, it returns |ssl_open_record_success|. It sets |*out_type| to the + * record type and |*out| to the record body in |in|. Note that |*out| may be + * empty. + * + * If a record was successfully processed but should be discarded, it returns + * |ssl_open_record_discard|. + * + * If a record was successfully processed but is a close_notify or fatal alert, + * it returns |ssl_open_record_close_notify| or |ssl_open_record_fatal_alert|. + * + * On failure, it returns |ssl_open_record_error| and sets |*out_alert| to an + * alert to emit. */ +enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, CBS *out, + size_t *out_consumed, uint8_t *out_alert, + uint8_t *in, size_t in_len); + +/* dtls_open_record implements |tls_open_record| for DTLS. It never returns + * |ssl_open_record_partial| but otherwise behaves analogously. */ +enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type, CBS *out, + size_t *out_consumed, + uint8_t *out_alert, uint8_t *in, + size_t in_len); + +/* ssl_seal_align_prefix_len returns the length of the prefix before the start + * of the bulk of the ciphertext when sealing a record with |ssl|. Callers may + * use this to align buffers. + * + * Note when TLS 1.0 CBC record-splitting is enabled, this includes the one byte + * record and is the offset into second record's ciphertext. Thus sealing a + * small record may result in a smaller output than this value. + * + * TODO(davidben): Is this alignment valuable? Record-splitting makes this a + * mess. */ +size_t ssl_seal_align_prefix_len(const SSL *ssl); + +/* tls_seal_record seals a new record of type |type| and body |in| and writes it + * to |out|. At most |max_out| bytes will be written. It returns one on success + * and zero on error. If enabled, |tls_seal_record| implements TLS 1.0 CBC 1/n-1 + * record splitting and may write two records concatenated. + * + * For a large record, the bulk of the ciphertext will begin + * |ssl_seal_align_prefix_len| bytes into out. Aligning |out| appropriately may + * improve performance. It writes at most |in_len| + |SSL_max_seal_overhead| + * bytes to |out|. + * + * |in| and |out| may not alias. */ +int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint8_t type, const uint8_t *in, size_t in_len); + +enum dtls1_use_epoch_t { + dtls1_use_previous_epoch, + dtls1_use_current_epoch, +}; + +/* dtls_max_seal_overhead returns the maximum overhead, in bytes, of sealing a + * record. */ +size_t dtls_max_seal_overhead(const SSL *ssl, enum dtls1_use_epoch_t use_epoch); + +/* dtls_seal_prefix_len returns the number of bytes of prefix to reserve in + * front of the plaintext when sealing a record in-place. */ +size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch); + +/* dtls_seal_record implements |tls_seal_record| for DTLS. |use_epoch| selects + * which epoch's cipher state to use. Unlike |tls_seal_record|, |in| and |out| + * may alias but, if they do, |in| must be exactly |dtls_seal_prefix_len| bytes + * ahead of |out|. */ +int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint8_t type, const uint8_t *in, size_t in_len, + enum dtls1_use_epoch_t use_epoch); + +/* ssl_process_alert processes |in| as an alert and updates |ssl|'s shutdown + * state. It returns one of |ssl_open_record_discard|, |ssl_open_record_error|, + * |ssl_open_record_close_notify|, or |ssl_open_record_fatal_alert| as + * appropriate. */ +enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert, + const uint8_t *in, size_t in_len); + + +/* Private key operations. */ + +/* ssl_has_private_key returns one if |ssl| has a private key + * configured and zero otherwise. */ +int ssl_has_private_key(const SSL *ssl); + +/* ssl_is_ecdsa_key_type returns one if |type| is an ECDSA key type and zero + * otherwise. */ +int ssl_is_ecdsa_key_type(int type); + +/* ssl_private_key_* call the corresponding function on the + * |SSL_PRIVATE_KEY_METHOD| for |ssl|, if configured. Otherwise, they implement + * the operation with |EVP_PKEY|. */ + +int ssl_private_key_type(SSL *ssl); + +size_t ssl_private_key_max_signature_len(SSL *ssl); + +enum ssl_private_key_result_t ssl_private_key_sign( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint16_t signature_algorithm, const uint8_t *in, size_t in_len); + +enum ssl_private_key_result_t ssl_private_key_decrypt( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + const uint8_t *in, size_t in_len); + +enum ssl_private_key_result_t ssl_private_key_complete(SSL *ssl, uint8_t *out, + size_t *out_len, + size_t max_out); + +/* ssl_private_key_supports_signature_algorithm returns one if |ssl|'s private + * key supports |signature_algorithm| and zero otherwise. */ +int ssl_private_key_supports_signature_algorithm(SSL *ssl, + uint16_t signature_algorithm); + +/* ssl_public_key_verify verifies that the |signature| is valid for the public + * key |pkey| and input |in|, using the |signature_algorithm| specified. */ +int ssl_public_key_verify( + SSL *ssl, const uint8_t *signature, size_t signature_len, + uint16_t signature_algorithm, EVP_PKEY *pkey, + const uint8_t *in, size_t in_len); + + +/* Custom extensions */ + +typedef struct ssl_handshake_st SSL_HANDSHAKE; + +/* ssl_custom_extension (a.k.a. SSL_CUSTOM_EXTENSION) is a structure that + * contains information about custom-extension callbacks. */ +struct ssl_custom_extension { + SSL_custom_ext_add_cb add_callback; + void *add_arg; + SSL_custom_ext_free_cb free_callback; + SSL_custom_ext_parse_cb parse_callback; + void *parse_arg; + uint16_t value; +}; + +void SSL_CUSTOM_EXTENSION_free(SSL_CUSTOM_EXTENSION *custom_extension); + +int custom_ext_add_clienthello(SSL_HANDSHAKE *hs, CBB *extensions); +int custom_ext_parse_serverhello(SSL_HANDSHAKE *hs, int *out_alert, + uint16_t value, const CBS *extension); +int custom_ext_parse_clienthello(SSL_HANDSHAKE *hs, int *out_alert, + uint16_t value, const CBS *extension); +int custom_ext_add_serverhello(SSL_HANDSHAKE *hs, CBB *extensions); + + +/* ECDH groups. */ + +typedef struct ssl_ecdh_ctx_st SSL_ECDH_CTX; + +/* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for + * TLS. */ +typedef struct ssl_ecdh_method_st { + int nid; + uint16_t group_id; + const char name[8]; + + /* cleanup releases state in |ctx|. */ + void (*cleanup)(SSL_ECDH_CTX *ctx); + + /* offer generates a keypair and writes the public value to + * |out_public_key|. It returns one on success and zero on error. */ + int (*offer)(SSL_ECDH_CTX *ctx, CBB *out_public_key); + + /* accept performs a key exchange against the |peer_key| generated by |offer|. + * On success, it returns one, writes the public value to |out_public_key|, + * and sets |*out_secret| and |*out_secret_len| to a newly-allocated buffer + * containing the shared secret. The caller must release this buffer with + * |OPENSSL_free|. On failure, it returns zero and sets |*out_alert| to an + * alert to send to the peer. */ + int (*accept)(SSL_ECDH_CTX *ctx, CBB *out_public_key, uint8_t **out_secret, + size_t *out_secret_len, uint8_t *out_alert, + const uint8_t *peer_key, size_t peer_key_len); + + /* finish performs a key exchange against the |peer_key| generated by + * |accept|. On success, it returns one and sets |*out_secret| and + * |*out_secret_len| to a newly-allocated buffer containing the shared + * secret. The caller must release this buffer with |OPENSSL_free|. On + * failure, it returns zero and sets |*out_alert| to an alert to send to the + * peer. */ + int (*finish)(SSL_ECDH_CTX *ctx, uint8_t **out_secret, size_t *out_secret_len, + uint8_t *out_alert, const uint8_t *peer_key, + size_t peer_key_len); + + /* get_key initializes |out| with a length-prefixed key from |cbs|. It returns + * one on success and zero on error. */ + int (*get_key)(CBS *cbs, CBS *out); + + /* add_key initializes |out_contents| to receive a key. Typically it will then + * be passed to |offer| or |accept|. It returns one on success and zero on + * error. */ + int (*add_key)(CBB *cbb, CBB *out_contents); +} SSL_ECDH_METHOD; + +struct ssl_ecdh_ctx_st { + const SSL_ECDH_METHOD *method; + void *data; +}; + +/* ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it + * sets |*out_group_id| to the group ID and returns one. Otherwise, it returns + * zero. */ +int ssl_nid_to_group_id(uint16_t *out_group_id, int nid); + +/* ssl_name_to_group_id looks up the group corresponding to the |name| string + * of length |len|. On success, it sets |*out_group_id| to the group ID and + * returns one. Otherwise, it returns zero. */ +int ssl_name_to_group_id(uint16_t *out_group_id, const char *name, size_t len); + +/* SSL_ECDH_CTX_init sets up |ctx| for use with curve |group_id|. It returns one + * on success and zero on error. */ +int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t group_id); + +/* SSL_ECDH_CTX_init_for_dhe sets up |ctx| for use with legacy DHE-based ciphers + * where the server specifies a group. It takes ownership of |params|. */ +void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params); + +/* SSL_ECDH_CTX_cleanup releases memory associated with |ctx|. It is legal to + * call it in the zero state. */ +void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx); + +/* SSL_ECDH_CTX_get_id returns the group ID for |ctx|. */ +uint16_t SSL_ECDH_CTX_get_id(const SSL_ECDH_CTX *ctx); + +/* SSL_ECDH_CTX_get_key calls the |get_key| method of |SSL_ECDH_METHOD|. */ +int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out); + +/* SSL_ECDH_CTX_add_key calls the |add_key| method of |SSL_ECDH_METHOD|. */ +int SSL_ECDH_CTX_add_key(SSL_ECDH_CTX *ctx, CBB *cbb, CBB *out_contents); + +/* SSL_ECDH_CTX_offer calls the |offer| method of |SSL_ECDH_METHOD|. */ +int SSL_ECDH_CTX_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key); + +/* SSL_ECDH_CTX_accept calls the |accept| method of |SSL_ECDH_METHOD|. */ +int SSL_ECDH_CTX_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key, + uint8_t **out_secret, size_t *out_secret_len, + uint8_t *out_alert, const uint8_t *peer_key, + size_t peer_key_len); + +/* SSL_ECDH_CTX_finish the |finish| method of |SSL_ECDH_METHOD|. */ +int SSL_ECDH_CTX_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret, + size_t *out_secret_len, uint8_t *out_alert, + const uint8_t *peer_key, size_t peer_key_len); + +/* Handshake messages. */ + +/* SSL_MAX_HANDSHAKE_FLIGHT is the number of messages, including + * ChangeCipherSpec, in the longest handshake flight. Currently this is the + * client's second leg in a full handshake when client certificates, NPN, and + * Channel ID, are all enabled. */ +#define SSL_MAX_HANDSHAKE_FLIGHT 7 + +/* ssl_max_handshake_message_len returns the maximum number of bytes permitted + * in a handshake message for |ssl|. */ +size_t ssl_max_handshake_message_len(const SSL *ssl); + +/* dtls_clear_incoming_messages releases all buffered incoming messages. */ +void dtls_clear_incoming_messages(SSL *ssl); + +/* dtls_has_incoming_messages returns one if there are buffered incoming + * messages ahead of the current message and zero otherwise. */ +int dtls_has_incoming_messages(const SSL *ssl); + +typedef struct dtls_outgoing_message_st { + uint8_t *data; + uint32_t len; + uint16_t epoch; + char is_ccs; +} DTLS_OUTGOING_MESSAGE; + +/* dtls_clear_outgoing_messages releases all buffered outgoing messages. */ +void dtls_clear_outgoing_messages(SSL *ssl); + + +/* Callbacks. */ + +/* ssl_do_info_callback calls |ssl|'s info callback, if set. */ +void ssl_do_info_callback(const SSL *ssl, int type, int value); + +/* ssl_do_msg_callback calls |ssl|'s message callback, if set. */ +void ssl_do_msg_callback(SSL *ssl, int is_write, int content_type, + const void *buf, size_t len); + + +/* Transport buffers. */ + +/* ssl_read_buffer returns a pointer to contents of the read buffer. */ +uint8_t *ssl_read_buffer(SSL *ssl); + +/* ssl_read_buffer_len returns the length of the read buffer. */ +size_t ssl_read_buffer_len(const SSL *ssl); + +/* ssl_read_buffer_extend_to extends the read buffer to the desired length. For + * TLS, it reads to the end of the buffer until the buffer is |len| bytes + * long. For DTLS, it reads a new packet and ignores |len|. It returns one on + * success, zero on EOF, and a negative number on error. + * + * It is an error to call |ssl_read_buffer_extend_to| in DTLS when the buffer is + * non-empty. */ +int ssl_read_buffer_extend_to(SSL *ssl, size_t len); + +/* ssl_read_buffer_consume consumes |len| bytes from the read buffer. It + * advances the data pointer and decrements the length. The memory consumed will + * remain valid until the next call to |ssl_read_buffer_extend| or it is + * discarded with |ssl_read_buffer_discard|. */ +void ssl_read_buffer_consume(SSL *ssl, size_t len); + +/* ssl_read_buffer_discard discards the consumed bytes from the read buffer. If + * the buffer is now empty, it releases memory used by it. */ +void ssl_read_buffer_discard(SSL *ssl); + +/* ssl_read_buffer_clear releases all memory associated with the read buffer and + * zero-initializes it. */ +void ssl_read_buffer_clear(SSL *ssl); + +/* ssl_write_buffer_is_pending returns one if the write buffer has pending data + * and zero if is empty. */ +int ssl_write_buffer_is_pending(const SSL *ssl); + +/* ssl_write_buffer_init initializes the write buffer. On success, it sets + * |*out_ptr| to the start of the write buffer with space for up to |max_len| + * bytes. It returns one on success and zero on failure. Call + * |ssl_write_buffer_set_len| to complete initialization. */ +int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len); + +/* ssl_write_buffer_set_len is called after |ssl_write_buffer_init| to complete + * initialization after |len| bytes are written to the buffer. */ +void ssl_write_buffer_set_len(SSL *ssl, size_t len); + +/* ssl_write_buffer_flush flushes the write buffer to the transport. It returns + * one on success and <= 0 on error. For DTLS, whether or not the write + * succeeds, the write buffer will be cleared. */ +int ssl_write_buffer_flush(SSL *ssl); + +/* ssl_write_buffer_clear releases all memory associated with the write buffer + * and zero-initializes it. */ +void ssl_write_buffer_clear(SSL *ssl); + + +/* Certificate functions. */ + +/* ssl_has_certificate returns one if a certificate and private key are + * configured and zero otherwise. */ +int ssl_has_certificate(const SSL *ssl); + +/* ssl_parse_cert_chain parses a certificate list from |cbs| in the format used + * by a TLS Certificate message. On success, it returns a newly-allocated + * |CRYPTO_BUFFER| list and advances |cbs|. Otherwise, it returns NULL and sets + * |*out_alert| to an alert to send to the peer. + * + * If the list is non-empty then |*out_pubkey| will be set to a freshly + * allocated public-key from the leaf certificate. + * + * If the list is non-empty and |out_leaf_sha256| is non-NULL, it writes the + * SHA-256 hash of the leaf to |out_leaf_sha256|. */ +STACK_OF(CRYPTO_BUFFER) *ssl_parse_cert_chain(uint8_t *out_alert, + EVP_PKEY **out_pubkey, + uint8_t *out_leaf_sha256, + CBS *cbs, + CRYPTO_BUFFER_POOL *pool); + +/* ssl_add_cert_chain adds |ssl|'s certificate chain to |cbb| in the format used + * by a TLS Certificate message. If there is no certificate chain, it emits an + * empty certificate list. It returns one on success and zero on error. */ +int ssl_add_cert_chain(SSL *ssl, CBB *cbb); + +/* ssl_cert_check_digital_signature_key_usage parses the DER-encoded, X.509 + * certificate in |in| and returns one if doesn't specify a key usage or, if it + * does, if it includes digitalSignature. Otherwise it pushes to the error + * queue and returns zero. */ +int ssl_cert_check_digital_signature_key_usage(const CBS *in); + +/* ssl_cert_parse_pubkey extracts the public key from the DER-encoded, X.509 + * certificate in |in|. It returns an allocated |EVP_PKEY| or else returns NULL + * and pushes to the error queue. */ +EVP_PKEY *ssl_cert_parse_pubkey(const CBS *in); + +/* ssl_parse_client_CA_list parses a CA list from |cbs| in the format used by a + * TLS CertificateRequest message. On success, it returns a newly-allocated + * |CRYPTO_BUFFER| list and advances |cbs|. Otherwise, it returns NULL and sets + * |*out_alert| to an alert to send to the peer. */ +STACK_OF(CRYPTO_BUFFER) * + ssl_parse_client_CA_list(SSL *ssl, uint8_t *out_alert, CBS *cbs); + +/* ssl_add_client_CA_list adds the configured CA list to |cbb| in the format + * used by a TLS CertificateRequest message. It returns one on success and zero + * on error. */ +int ssl_add_client_CA_list(SSL *ssl, CBB *cbb); + +/* ssl_check_leaf_certificate returns one if |pkey| and |leaf| are suitable as + * a server's leaf certificate for |hs|. Otherwise, it returns zero and pushes + * an error on the error queue. */ +int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey, + const CRYPTO_BUFFER *leaf); + + +/* TLS 1.3 key derivation. */ + +/* tls13_init_key_schedule initializes the handshake hash and key derivation + * state. The cipher suite and PRF hash must have been selected at this point. + * It returns one on success and zero on error. */ +int tls13_init_key_schedule(SSL_HANDSHAKE *hs); + +/* tls13_advance_key_schedule incorporates |in| into the key schedule with + * HKDF-Extract. It returns one on success and zero on error. */ +int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in, + size_t len); + +/* tls13_set_traffic_key sets the read or write traffic keys to + * |traffic_secret|. It returns one on success and zero on error. */ +int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction, + const uint8_t *traffic_secret, + size_t traffic_secret_len); + +/* tls13_derive_handshake_secrets derives the handshake traffic secret. It + * returns one on success and zero on error. */ +int tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs); + +/* tls13_rotate_traffic_key derives the next read or write traffic secret. It + * returns one on success and zero on error. */ +int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction); + +/* tls13_derive_application_secrets derives the initial application data traffic + * and exporter secrets based on the handshake transcripts and |master_secret|. + * It returns one on success and zero on error. */ +int tls13_derive_application_secrets(SSL_HANDSHAKE *hs); + +/* tls13_derive_resumption_secret derives the |resumption_secret|. */ +int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs); + +/* tls13_export_keying_material provides an exporter interface to use the + * |exporter_secret|. */ +int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len, + const char *label, size_t label_len, + const uint8_t *context, size_t context_len, + int use_context); + +/* tls13_finished_mac calculates the MAC of the handshake transcript to verify + * the integrity of the Finished message, and stores the result in |out| and + * length in |out_len|. |is_server| is 1 if this is for the Server Finished and + * 0 for the Client Finished. */ +int tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, + size_t *out_len, int is_server); + +/* tls13_write_psk_binder calculates the PSK binder value and replaces the last + * bytes of |msg| with the resulting value. It returns 1 on success, and 0 on + * failure. */ +int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len); + +/* tls13_verify_psk_binder verifies that the handshake transcript, truncated + * up to the binders has a valid signature using the value of |session|'s + * resumption secret. It returns 1 on success, and 0 on failure. */ +int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session, + CBS *binders); + + +/* Handshake functions. */ + +enum ssl_hs_wait_t { + ssl_hs_error, + ssl_hs_ok, + ssl_hs_read_message, + ssl_hs_flush, + ssl_hs_flush_and_read_message, + ssl_hs_x509_lookup, + ssl_hs_channel_id_lookup, + ssl_hs_private_key_operation, + ssl_hs_pending_ticket, +}; + +struct ssl_handshake_st { + /* ssl is a non-owning pointer to the parent |SSL| object. */ + SSL *ssl; + + /* do_tls13_handshake runs the TLS 1.3 handshake. On completion, it returns + * |ssl_hs_ok|. Otherwise, it returns a value corresponding to what operation + * is needed to progress. */ + enum ssl_hs_wait_t (*do_tls13_handshake)(SSL_HANDSHAKE *hs); + + /* wait contains the operation |do_tls13_handshake| is currently blocking on + * or |ssl_hs_ok| if none. */ + enum ssl_hs_wait_t wait; + + /* state contains one of the SSL3_ST_* values. */ + int state; + + /* next_state is used when SSL_ST_FLUSH_DATA is entered */ + int next_state; + + /* tls13_state is the internal state for the TLS 1.3 handshake. Its values + * depend on |do_tls13_handshake| but the starting state is always zero. */ + int tls13_state; + + size_t hash_len; + uint8_t secret[EVP_MAX_MD_SIZE]; + uint8_t client_handshake_secret[EVP_MAX_MD_SIZE]; + uint8_t server_handshake_secret[EVP_MAX_MD_SIZE]; + uint8_t client_traffic_secret_0[EVP_MAX_MD_SIZE]; + uint8_t server_traffic_secret_0[EVP_MAX_MD_SIZE]; + + union { + /* sent is a bitset where the bits correspond to elements of kExtensions + * in t1_lib.c. Each bit is set if that extension was sent in a + * ClientHello. It's not used by servers. */ + uint32_t sent; + /* received is a bitset, like |sent|, but is used by servers to record + * which extensions were received from a client. */ + uint32_t received; + } extensions; + + union { + /* sent is a bitset where the bits correspond to elements of + * |client_custom_extensions| in the |SSL_CTX|. Each bit is set if that + * extension was sent in a ClientHello. It's not used by servers. */ + uint16_t sent; + /* received is a bitset, like |sent|, but is used by servers to record + * which custom extensions were received from a client. The bits here + * correspond to |server_custom_extensions|. */ + uint16_t received; + } custom_extensions; + + /* retry_group is the group ID selected by the server in HelloRetryRequest in + * TLS 1.3. */ + uint16_t retry_group; + + /* ecdh_ctx is the current ECDH instance. */ + SSL_ECDH_CTX ecdh_ctx; + + /* transcript is the current handshake transcript. */ + SSL_TRANSCRIPT transcript; + + /* cookie is the value of the cookie received from the server, if any. */ + uint8_t *cookie; + size_t cookie_len; + + /* key_share_bytes is the value of the previously sent KeyShare extension by + * the client in TLS 1.3. */ + uint8_t *key_share_bytes; + size_t key_share_bytes_len; + + /* public_key, for servers, is the key share to be sent to the client in TLS + * 1.3. */ + uint8_t *public_key; + size_t public_key_len; + + /* peer_sigalgs are the signature algorithms that the peer supports. These are + * taken from the contents of the signature algorithms extension for a server + * or from the CertificateRequest for a client. */ + uint16_t *peer_sigalgs; + /* num_peer_sigalgs is the number of entries in |peer_sigalgs|. */ + size_t num_peer_sigalgs; + + /* peer_supported_group_list contains the supported group IDs advertised by + * the peer. This is only set on the server's end. The server does not + * advertise this extension to the client. */ + uint16_t *peer_supported_group_list; + size_t peer_supported_group_list_len; + + /* peer_key is the peer's ECDH key for a TLS 1.2 client. */ + uint8_t *peer_key; + size_t peer_key_len; + + /* server_params, in TLS 1.2, stores the ServerKeyExchange parameters to be + * signed while the signature is being computed. */ + uint8_t *server_params; + size_t server_params_len; + + /* peer_psk_identity_hint, on the client, is the psk_identity_hint sent by the + * server when using a TLS 1.2 PSK key exchange. */ + char *peer_psk_identity_hint; + + /* ca_names, on the client, contains the list of CAs received in a + * CertificateRequest message. */ + STACK_OF(CRYPTO_BUFFER) *ca_names; + + /* cached_x509_ca_names contains a cache of parsed versions of the elements + * of |ca_names|. */ + STACK_OF(X509_NAME) *cached_x509_ca_names; + + /* certificate_types, on the client, contains the set of certificate types + * received in a CertificateRequest message. */ + uint8_t *certificate_types; + size_t num_certificate_types; + + /* hostname, on the server, is the value of the SNI extension. */ + char *hostname; + + /* peer_pubkey is the public key parsed from the peer's leaf certificate. */ + EVP_PKEY *peer_pubkey; + + /* new_session is the new mutable session being established by the current + * handshake. It should not be cached. */ + SSL_SESSION *new_session; + + /* new_cipher is the cipher being negotiated in this handshake. */ + const SSL_CIPHER *new_cipher; + + /* key_block is the record-layer key block for TLS 1.2 and earlier. */ + uint8_t *key_block; + uint8_t key_block_len; + + /* session_tickets_sent, in TLS 1.3, is the number of tickets the server has + * sent. */ + uint8_t session_tickets_sent; + + /* scts_requested is one if the SCT extension is in the ClientHello. */ + unsigned scts_requested:1; + + /* needs_psk_binder if the ClientHello has a placeholder PSK binder to be + * filled in. */ + unsigned needs_psk_binder:1; + + unsigned received_hello_retry_request:1; + + /* accept_psk_mode stores whether the client's PSK mode is compatible with our + * preferences. */ + unsigned accept_psk_mode:1; + + /* cert_request is one if a client certificate was requested and zero + * otherwise. */ + unsigned cert_request:1; + + /* certificate_status_expected is one if OCSP stapling was negotiated and the + * server is expected to send a CertificateStatus message. (This is used on + * both the client and server sides.) */ + unsigned certificate_status_expected:1; + + /* ocsp_stapling_requested is one if a client requested OCSP stapling. */ + unsigned ocsp_stapling_requested:1; + + /* should_ack_sni is used by a server and indicates that the SNI extension + * should be echoed in the ServerHello. */ + unsigned should_ack_sni:1; + + /* in_false_start is one if there is a pending client handshake in False + * Start. The client may write data at this point. */ + unsigned in_false_start:1; + + /* next_proto_neg_seen is one of NPN was negotiated. */ + unsigned next_proto_neg_seen:1; + + /* ticket_expected is one if a TLS 1.2 NewSessionTicket message is to be sent + * or received. */ + unsigned ticket_expected:1; + + /* v2_clienthello is one if we received a V2ClientHello. */ + unsigned v2_clienthello:1; + + /* extended_master_secret is one if the extended master secret extension is + * negotiated in this handshake. */ + unsigned extended_master_secret:1; + + /* client_version is the value sent or received in the ClientHello version. */ + uint16_t client_version; +} /* SSL_HANDSHAKE */; + +SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl); + +/* ssl_handshake_free releases all memory associated with |hs|. */ +void ssl_handshake_free(SSL_HANDSHAKE *hs); + +/* ssl_check_message_type checks if the current message has type |type|. If so + * it returns one. Otherwise, it sends an alert and returns zero. */ +int ssl_check_message_type(SSL *ssl, int type); + +/* tls13_handshake runs the TLS 1.3 handshake. It returns one on success and <= + * 0 on error. */ +int tls13_handshake(SSL_HANDSHAKE *hs); + +/* The following are implementations of |do_tls13_handshake| for the client and + * server. */ +enum ssl_hs_wait_t tls13_client_handshake(SSL_HANDSHAKE *hs); +enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs); + +/* tls13_post_handshake processes a post-handshake message. It returns one on + * success and zero on failure. */ +int tls13_post_handshake(SSL *ssl); + +int tls13_process_certificate(SSL_HANDSHAKE *hs, int allow_anonymous); +int tls13_process_certificate_verify(SSL_HANDSHAKE *hs); +int tls13_process_finished(SSL_HANDSHAKE *hs); + +int tls13_add_certificate(SSL_HANDSHAKE *hs); +enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs, + int is_first_run); +int tls13_add_finished(SSL_HANDSHAKE *hs); +int tls13_process_new_session_ticket(SSL *ssl); + +int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t **out_secret, + size_t *out_secret_len, + uint8_t *out_alert, CBS *contents); +int ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, int *out_found, + uint8_t **out_secret, + size_t *out_secret_len, + uint8_t *out_alert, CBS *contents); +int ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out); + +int ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, CBS *contents); +int ssl_ext_pre_shared_key_parse_clienthello( + SSL_HANDSHAKE *hs, CBS *out_ticket, CBS *out_binders, + uint32_t *out_obfuscated_ticket_age, uint8_t *out_alert, CBS *contents); +int ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out); + +/* ssl_is_sct_list_valid does a shallow parse of the SCT list in |contents| and + * returns one iff it's valid. */ +int ssl_is_sct_list_valid(const CBS *contents); + +int ssl_write_client_hello(SSL_HANDSHAKE *hs); + +/* ssl_clear_tls13_state releases client state only needed for TLS 1.3. It + * should be called once the version is known to be TLS 1.2 or earlier. */ +void ssl_clear_tls13_state(SSL_HANDSHAKE *hs); + +enum ssl_cert_verify_context_t { + ssl_cert_verify_server, + ssl_cert_verify_client, + ssl_cert_verify_channel_id, +}; + +/* tls13_get_cert_verify_signature_input generates the message to be signed for + * TLS 1.3's CertificateVerify message. |cert_verify_context| determines the + * type of signature. It sets |*out| and |*out_len| to a newly allocated buffer + * containing the result. The caller must free it with |OPENSSL_free| to release + * it. This function returns one on success and zero on failure. */ +int tls13_get_cert_verify_signature_input( + SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len, + enum ssl_cert_verify_context_t cert_verify_context); + +/* ssl_negotiate_alpn negotiates the ALPN extension, if applicable. It returns + * one on successful negotiation or if nothing was negotiated. It returns zero + * and sets |*out_alert| to an alert on error. */ +int ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert, + const SSL_CLIENT_HELLO *client_hello); + +typedef struct { + uint16_t type; + int *out_present; + CBS *out_data; +} SSL_EXTENSION_TYPE; + +/* ssl_parse_extensions parses a TLS extensions block out of |cbs| and advances + * it. It writes the parsed extensions to pointers denoted by |ext_types|. On + * success, it fills in the |out_present| and |out_data| fields and returns one. + * Otherwise, it sets |*out_alert| to an alert to send and returns zero. Unknown + * extensions are rejected unless |ignore_unknown| is 1. */ +int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert, + const SSL_EXTENSION_TYPE *ext_types, + size_t num_ext_types, int ignore_unknown); + + +/* SSLKEYLOGFILE functions. */ + +/* ssl_log_secret logs |secret| with label |label|, if logging is enabled for + * |ssl|. It returns one on success and zero on failure. */ +int ssl_log_secret(const SSL *ssl, const char *label, const uint8_t *secret, + size_t secret_len); + + +/* ClientHello functions. */ + +int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out, const uint8_t *in, + size_t in_len); + +int ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello, + CBS *out, uint16_t extension_type); + +int ssl_client_cipher_list_contains_cipher(const SSL_CLIENT_HELLO *client_hello, + uint16_t id); + + +/* GREASE. */ + +enum ssl_grease_index_t { + ssl_grease_cipher = 0, + ssl_grease_group, + ssl_grease_extension1, + ssl_grease_extension2, + ssl_grease_version, + ssl_grease_ticket_extension, +}; + +/* ssl_get_grease_value returns a GREASE value for |ssl|. For a given + * connection, the values for each index will be deterministic. This allows the + * same ClientHello be sent twice for a HelloRetryRequest or the same group be + * advertised in both supported_groups and key_shares. */ +uint16_t ssl_get_grease_value(const SSL *ssl, enum ssl_grease_index_t index); + + +/* Signature algorithms. */ + +/* tls1_parse_peer_sigalgs parses |sigalgs| as the list of peer signature + * algorithms and saves them on |hs|. It returns one on success and zero on + * error. */ +int tls1_parse_peer_sigalgs(SSL_HANDSHAKE *hs, const CBS *sigalgs); + +/* tls1_choose_signature_algorithm sets |*out| to a signature algorithm for use + * with |hs|'s private key based on the peer's preferences and the algorithms + * supported. It returns one on success and zero on error. */ +int tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out); + +/* tls12_get_verify_sigalgs sets |*out| to the signature algorithms acceptable + * for the peer signature and returns the length of the list. */ +size_t tls12_get_verify_sigalgs(const SSL *ssl, const uint16_t **out); + +/* tls12_check_peer_sigalg checks if |sigalg| is acceptable for the peer + * signature. It returns one on success and zero on error, setting |*out_alert| + * to an alert to send. */ +int tls12_check_peer_sigalg(SSL *ssl, int *out_alert, uint16_t sigalg); + + +/* Underdocumented functions. + * + * Functions below here haven't been touched up and may be underdocumented. */ + +#define TLSEXT_CHANNEL_ID_SIZE 128 + +/* From RFC4492, used in encoding the curve type in ECParameters */ +#define NAMED_CURVE_TYPE 3 + +typedef struct cert_st { + EVP_PKEY *privatekey; + + /* chain contains the certificate chain, with the leaf at the beginning. The + * first element of |chain| may be NULL to indicate that the leaf certificate + * has not yet been set. + * If |chain| != NULL -> len(chain) >= 1 + * If |chain[0]| == NULL -> len(chain) >= 2. + * |chain[1..]| != NULL */ + STACK_OF(CRYPTO_BUFFER) *chain; + + /* x509_chain may contain a parsed copy of |chain[1..]|. This is only used as + * a cache in order to implement “get0” functions that return a non-owning + * pointer to the certificate chain. */ + STACK_OF(X509) *x509_chain; + + /* x509_leaf may contain a parsed copy of the first element of |chain|. This + * is only used as a cache in order to implement “get0” functions that return + * a non-owning pointer to the certificate chain. */ + X509 *x509_leaf; + + /* x509_stash contains the last |X509| object append to the chain. This is a + * workaround for some third-party code that continue to use an |X509| object + * even after passing ownership with an “add0” function. */ + X509 *x509_stash; + + /* key_method, if non-NULL, is a set of callbacks to call for private key + * operations. */ + const SSL_PRIVATE_KEY_METHOD *key_method; + + /* x509_method contains pointers to functions that might deal with |X509| + * compatibility, or might be a no-op, depending on the application. */ + const SSL_X509_METHOD *x509_method; + + DH *dh_tmp; + DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize); + + /* sigalgs, if non-NULL, is the set of signature algorithms supported by + * |privatekey| in decreasing order of preference. */ + uint16_t *sigalgs; + size_t num_sigalgs; + + /* Certificate setup callback: if set is called whenever a + * certificate may be required (client or server). the callback + * can then examine any appropriate parameters and setup any + * certificates required. This allows advanced applications + * to select certificates on the fly: for example based on + * supported signature algorithms or curves. */ + int (*cert_cb)(SSL *ssl, void *arg); + void *cert_cb_arg; + + /* Optional X509_STORE for certificate validation. If NULL the parent SSL_CTX + * store is used instead. */ + X509_STORE *verify_store; + + /* Signed certificate timestamp list to be sent to the client, if requested */ + CRYPTO_BUFFER *signed_cert_timestamp_list; + + /* OCSP response to be sent to the client, if requested. */ + CRYPTO_BUFFER *ocsp_response; + + /* sid_ctx partitions the session space within a shared session cache or + * ticket key. Only sessions with a matching value will be accepted. */ + uint8_t sid_ctx_length; + uint8_t sid_ctx[SSL_MAX_SID_CTX_LENGTH]; +} CERT; + +/* SSL_METHOD is a compatibility structure to support the legacy version-locked + * methods. */ +struct ssl_method_st { + /* version, if non-zero, is the only protocol version acceptable to an + * SSL_CTX initialized from this method. */ + uint16_t version; + /* method is the underlying SSL_PROTOCOL_METHOD that initializes the + * SSL_CTX. */ + const SSL_PROTOCOL_METHOD *method; + /* x509_method contains pointers to functions that might deal with |X509| + * compatibility, or might be a no-op, depending on the application. */ + const SSL_X509_METHOD *x509_method; +}; + +/* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ +struct ssl_protocol_method_st { + /* is_dtls is one if the protocol is DTLS and zero otherwise. */ + char is_dtls; + /* min_version is the minimum implemented version. */ + uint16_t min_version; + /* max_version is the maximum implemented version. */ + uint16_t max_version; + /* version_from_wire maps |wire_version| to a protocol version. On success, it + * sets |*out_version| to the result and returns one. If the version is + * unknown, it returns zero. */ + int (*version_from_wire)(uint16_t *out_version, uint16_t wire_version); + /* version_to_wire maps |version| to the wire representation. It is an error + * to call it with an invalid version. */ + uint16_t (*version_to_wire)(uint16_t version); + int (*ssl_new)(SSL *ssl); + void (*ssl_free)(SSL *ssl); + /* ssl_get_message reads the next handshake message. On success, it returns + * one and sets |ssl->s3->tmp.message_type|, |ssl->init_msg|, and + * |ssl->init_num|. Otherwise, it returns <= 0. */ + int (*ssl_get_message)(SSL *ssl); + /* get_current_message sets |*out| to the current handshake message. This + * includes the protocol-specific message header. */ + void (*get_current_message)(const SSL *ssl, CBS *out); + /* release_current_message is called to release the current handshake message. + * If |free_buffer| is one, buffers will also be released. */ + void (*release_current_message)(SSL *ssl, int free_buffer); + /* read_app_data reads up to |len| bytes of application data into |buf|. On + * success, it returns the number of bytes read. Otherwise, it returns <= 0 + * and sets |*out_got_handshake| to whether the failure was due to a + * post-handshake handshake message. If so, it fills in the current message as + * in |ssl_get_message|. */ + int (*read_app_data)(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len, + int peek); + int (*read_change_cipher_spec)(SSL *ssl); + void (*read_close_notify)(SSL *ssl); + int (*write_app_data)(SSL *ssl, const uint8_t *buf, int len); + int (*dispatch_alert)(SSL *ssl); + /* supports_cipher returns one if |cipher| is supported by this protocol and + * zero otherwise. */ + int (*supports_cipher)(const SSL_CIPHER *cipher); + /* init_message begins a new handshake message of type |type|. |cbb| is the + * root CBB to be passed into |finish_message|. |*body| is set to a child CBB + * the caller should write to. It returns one on success and zero on error. */ + int (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); + /* finish_message finishes a handshake message. It sets |*out_msg| to a + * newly-allocated buffer with the serialized message. The caller must + * release it with |OPENSSL_free| when done. It returns one on success and + * zero on error. */ + int (*finish_message)(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len); + /* add_message adds a handshake message to the pending flight. It returns one + * on success and zero on error. In either case, it takes ownership of |msg| + * and releases it with |OPENSSL_free| when done. */ + int (*add_message)(SSL *ssl, uint8_t *msg, size_t len); + /* add_change_cipher_spec adds a ChangeCipherSpec record to the pending + * flight. It returns one on success and zero on error. */ + int (*add_change_cipher_spec)(SSL *ssl); + /* add_alert adds an alert to the pending flight. It returns one on success + * and zero on error. */ + int (*add_alert)(SSL *ssl, uint8_t level, uint8_t desc); + /* flush_flight flushes the pending flight to the transport. It returns one on + * success and <= 0 on error. */ + int (*flush_flight)(SSL *ssl); + /* expect_flight is called when the handshake expects a flight of messages from + * the peer. */ + void (*expect_flight)(SSL *ssl); + /* received_flight is called when the handshake has received a flight of + * messages from the peer. */ + void (*received_flight)(SSL *ssl); + /* set_read_state sets |ssl|'s read cipher state to |aead_ctx|. It takes + * ownership of |aead_ctx|. It returns one on success and zero if changing the + * read state is forbidden at this point. */ + int (*set_read_state)(SSL *ssl, SSL_AEAD_CTX *aead_ctx); + /* set_write_state sets |ssl|'s write cipher state to |aead_ctx|. It takes + * ownership of |aead_ctx|. It returns one on success and zero if changing the + * write state is forbidden at this point. */ + int (*set_write_state)(SSL *ssl, SSL_AEAD_CTX *aead_ctx); +}; + +struct ssl_x509_method_st { + /* check_client_CA_list returns one if |names| is a good list of X.509 + * distinguished names and zero otherwise. This is used to ensure that we can + * reject unparsable values at handshake time when using crypto/x509. */ + int (*check_client_CA_list)(STACK_OF(CRYPTO_BUFFER) *names); + + /* cert_clear frees and NULLs all X509 certificate-related state. */ + void (*cert_clear)(CERT *cert); + /* cert_free frees all X509-related state. */ + void (*cert_free)(CERT *cert); + /* cert_flush_cached_chain drops any cached |X509|-based certificate chain + * from |cert|. */ + /* cert_dup duplicates any needed fields from |cert| to |new_cert|. */ + void (*cert_dup)(CERT *new_cert, const CERT *cert); + void (*cert_flush_cached_chain)(CERT *cert); + /* cert_flush_cached_chain drops any cached |X509|-based leaf certificate + * from |cert|. */ + void (*cert_flush_cached_leaf)(CERT *cert); + + /* session_cache_objects fills out |sess->x509_peer| and |sess->x509_chain| + * from |sess->certs| and erases |sess->x509_chain_without_leaf|. It returns + * one on success or zero on error. */ + int (*session_cache_objects)(SSL_SESSION *session); + /* session_dup duplicates any needed fields from |session| to |new_session|. + * It returns one on success or zero on error. */ + int (*session_dup)(SSL_SESSION *new_session, const SSL_SESSION *session); + /* session_clear frees any X509-related state from |session|. */ + void (*session_clear)(SSL_SESSION *session); + /* session_verify_cert_chain verifies the certificate chain in |session|, + * sets |session->verify_result| and returns one on success or zero on + * error. */ + int (*session_verify_cert_chain)(SSL_SESSION *session, SSL *ssl); + + /* hs_flush_cached_ca_names drops any cached |X509_NAME|s from |hs|. */ + void (*hs_flush_cached_ca_names)(SSL_HANDSHAKE *hs); + /* ssl_new does any neccessary initialisation of |ssl|. It returns one on + * success or zero on error. */ + int (*ssl_new)(SSL *ssl); + /* ssl_free frees anything created by |ssl_new|. */ + void (*ssl_free)(SSL *ssl); + /* ssl_flush_cached_client_CA drops any cached |X509_NAME|s from |ssl|. */ + void (*ssl_flush_cached_client_CA)(SSL *ssl); + /* ssl_auto_chain_if_needed runs the deprecated auto-chaining logic if + * necessary. On success, it updates |ssl|'s certificate configuration as + * needed and returns one. Otherwise, it returns zero. */ + int (*ssl_auto_chain_if_needed)(SSL *ssl); + /* ssl_ctx_new does any neccessary initialisation of |ctx|. It returns one on + * success or zero on error. */ + int (*ssl_ctx_new)(SSL_CTX *ctx); + /* ssl_ctx_free frees anything created by |ssl_ctx_new|. */ + void (*ssl_ctx_free)(SSL_CTX *ctx); + /* ssl_ctx_flush_cached_client_CA drops any cached |X509_NAME|s from |ctx|. */ + void (*ssl_ctx_flush_cached_client_CA)(SSL_CTX *ssl); +}; + +/* ssl_crypto_x509_method provides the |ssl_x509_method_st| functions using + * crypto/x509. */ +extern const struct ssl_x509_method_st ssl_crypto_x509_method; + +typedef struct ssl3_record_st { + /* type is the record type. */ + uint8_t type; + /* length is the number of unconsumed bytes in the record. */ + uint16_t length; + /* data is a non-owning pointer to the first unconsumed byte of the record. */ + uint8_t *data; +} SSL3_RECORD; + +typedef struct ssl3_buffer_st { + /* buf is the memory allocated for this buffer. */ + uint8_t *buf; + /* offset is the offset into |buf| which the buffer contents start at. */ + uint16_t offset; + /* len is the length of the buffer contents from |buf| + |offset|. */ + uint16_t len; + /* cap is how much memory beyond |buf| + |offset| is available. */ + uint16_t cap; +} SSL3_BUFFER; + +/* An ssl_shutdown_t describes the shutdown state of one end of the connection, + * whether it is alive or has been shutdown via close_notify or fatal alert. */ +enum ssl_shutdown_t { + ssl_shutdown_none = 0, + ssl_shutdown_close_notify = 1, + ssl_shutdown_fatal_alert = 2, +}; + +typedef struct ssl3_state_st { + uint8_t read_sequence[8]; + uint8_t write_sequence[8]; + + uint8_t server_random[SSL3_RANDOM_SIZE]; + uint8_t client_random[SSL3_RANDOM_SIZE]; + + /* read_buffer holds data from the transport to be processed. */ + SSL3_BUFFER read_buffer; + /* write_buffer holds data to be written to the transport. */ + SSL3_BUFFER write_buffer; + + SSL3_RECORD rrec; /* each decoded record goes in here */ + + /* partial write - check the numbers match */ + unsigned int wnum; /* number of bytes sent so far */ + int wpend_tot; /* number bytes written */ + int wpend_type; + int wpend_ret; /* number of bytes submitted */ + const uint8_t *wpend_buf; + + /* recv_shutdown is the shutdown state for the receive half of the + * connection. */ + enum ssl_shutdown_t recv_shutdown; + + /* recv_shutdown is the shutdown state for the send half of the connection. */ + enum ssl_shutdown_t send_shutdown; + + int alert_dispatch; + + int total_renegotiations; + + /* early_data_skipped is the amount of early data that has been skipped by the + * record layer. */ + uint16_t early_data_skipped; + + /* empty_record_count is the number of consecutive empty records received. */ + uint8_t empty_record_count; + + /* warning_alert_count is the number of consecutive warning alerts + * received. */ + uint8_t warning_alert_count; + + /* key_update_count is the number of consecutive KeyUpdates received. */ + uint8_t key_update_count; + + /* skip_early_data instructs the record layer to skip unexpected early data + * messages when 0RTT is rejected. */ + unsigned skip_early_data:1; + + /* have_version is true if the connection's final version is known. Otherwise + * the version has not been negotiated yet. */ + unsigned have_version:1; + + /* v2_hello_done is true if the peer's V2ClientHello, if any, has been handled + * and future messages should use the record layer. */ + unsigned v2_hello_done:1; + + /* is_v2_hello is true if the current handshake message was derived from a + * V2ClientHello rather than received from the peer directly. */ + unsigned is_v2_hello:1; + + /* initial_handshake_complete is true if the initial handshake has + * completed. */ + unsigned initial_handshake_complete:1; + + /* session_reused indicates whether a session was resumed. */ + unsigned session_reused:1; + + unsigned send_connection_binding:1; + + /* In a client, this means that the server supported Channel ID and that a + * Channel ID was sent. In a server it means that we echoed support for + * Channel IDs and that tlsext_channel_id will be valid after the + * handshake. */ + unsigned tlsext_channel_id_valid:1; + + uint8_t send_alert[2]; + + /* pending_flight is the pending outgoing flight. This is used to flush each + * handshake flight in a single write. */ + BUF_MEM *pending_flight; + + /* pending_flight_offset is the number of bytes of |pending_flight| which have + * been successfully written. */ + uint32_t pending_flight_offset; + + /* aead_read_ctx is the current read cipher state. */ + SSL_AEAD_CTX *aead_read_ctx; + + /* aead_write_ctx is the current write cipher state. */ + SSL_AEAD_CTX *aead_write_ctx; + + /* hs is the handshake state for the current handshake or NULL if there isn't + * one. */ + SSL_HANDSHAKE *hs; + + uint8_t write_traffic_secret[EVP_MAX_MD_SIZE]; + uint8_t read_traffic_secret[EVP_MAX_MD_SIZE]; + uint8_t exporter_secret[EVP_MAX_MD_SIZE]; + uint8_t write_traffic_secret_len; + uint8_t read_traffic_secret_len; + uint8_t exporter_secret_len; + + /* Connection binding to prevent renegotiation attacks */ + uint8_t previous_client_finished[12]; + uint8_t previous_client_finished_len; + uint8_t previous_server_finished_len; + uint8_t previous_server_finished[12]; + + /* State pertaining to the pending handshake. + * + * TODO(davidben): Move everything not needed after the handshake completes to + * |hs| and remove this. */ + struct { + int message_type; + + int reuse_message; + + uint8_t new_mac_secret_len; + uint8_t new_key_len; + uint8_t new_fixed_iv_len; + } tmp; + + /* established_session is the session established by the connection. This + * session is only filled upon the completion of the handshake and is + * immutable. */ + SSL_SESSION *established_session; + + /* Next protocol negotiation. For the client, this is the protocol that we + * sent in NextProtocol and is set when handling ServerHello extensions. + * + * For a server, this is the client's selected_protocol from NextProtocol and + * is set when handling the NextProtocol message, before the Finished + * message. */ + uint8_t *next_proto_negotiated; + size_t next_proto_negotiated_len; + + /* ALPN information + * (we are in the process of transitioning from NPN to ALPN.) */ + + /* In a server these point to the selected ALPN protocol after the + * ClientHello has been processed. In a client these contain the protocol + * that the server selected once the ServerHello has been processed. */ + uint8_t *alpn_selected; + size_t alpn_selected_len; + + /* For a server: + * If |tlsext_channel_id_valid| is true, then this contains the + * verified Channel ID from the client: a P256 point, (x,y), where + * each are big-endian values. */ + uint8_t tlsext_channel_id[64]; + + /* ticket_age_skew is the difference, in seconds, between the client-sent + * ticket age and the server-computed value in TLS 1.3 server connections + * which resumed a session. */ + int32_t ticket_age_skew; +} SSL3_STATE; + +/* lengths of messages */ +#define DTLS1_COOKIE_LENGTH 256 + +#define DTLS1_RT_HEADER_LENGTH 13 + +#define DTLS1_HM_HEADER_LENGTH 12 + +#define DTLS1_CCS_HEADER_LENGTH 1 + +#define DTLS1_AL_HEADER_LENGTH 2 + +struct hm_header_st { + uint8_t type; + uint32_t msg_len; + uint16_t seq; + uint32_t frag_off; + uint32_t frag_len; +}; + +/* An hm_fragment is an incoming DTLS message, possibly not yet assembled. */ +typedef struct hm_fragment_st { + /* type is the type of the message. */ + uint8_t type; + /* seq is the sequence number of this message. */ + uint16_t seq; + /* msg_len is the length of the message body. */ + uint32_t msg_len; + /* data is a pointer to the message, including message header. It has length + * |DTLS1_HM_HEADER_LENGTH| + |msg_len|. */ + uint8_t *data; + /* reassembly is a bitmask of |msg_len| bits corresponding to which parts of + * the message have been received. It is NULL if the message is complete. */ + uint8_t *reassembly; +} hm_fragment; + +struct OPENSSL_timeval { + uint64_t tv_sec; + uint32_t tv_usec; +}; + +typedef struct dtls1_state_st { + /* send_cookie is true if we are resending the ClientHello + * with a cookie from a HelloVerifyRequest. */ + unsigned int send_cookie; + + uint8_t cookie[DTLS1_COOKIE_LENGTH]; + size_t cookie_len; + + /* The current data and handshake epoch. This is initially undefined, and + * starts at zero once the initial handshake is completed. */ + uint16_t r_epoch; + uint16_t w_epoch; + + /* records being received in the current epoch */ + DTLS1_BITMAP bitmap; + + uint16_t handshake_write_seq; + uint16_t handshake_read_seq; + + /* save last sequence number for retransmissions */ + uint8_t last_write_sequence[8]; + + /* incoming_messages is a ring buffer of incoming handshake messages that have + * yet to be processed. The front of the ring buffer is message number + * |handshake_read_seq|, at position |handshake_read_seq| % + * |SSL_MAX_HANDSHAKE_FLIGHT|. */ + hm_fragment *incoming_messages[SSL_MAX_HANDSHAKE_FLIGHT]; + + /* outgoing_messages is the queue of outgoing messages from the last handshake + * flight. */ + DTLS_OUTGOING_MESSAGE outgoing_messages[SSL_MAX_HANDSHAKE_FLIGHT]; + uint8_t outgoing_messages_len; + + /* outgoing_written is the number of outgoing messages that have been + * written. */ + uint8_t outgoing_written; + /* outgoing_offset is the number of bytes of the next outgoing message have + * been written. */ + uint32_t outgoing_offset; + + unsigned int mtu; /* max DTLS packet size */ + + /* num_timeouts is the number of times the retransmit timer has fired since + * the last time it was reset. */ + unsigned int num_timeouts; + + /* Indicates when the last handshake msg or heartbeat sent will + * timeout. */ + struct OPENSSL_timeval next_timeout; + + /* timeout_duration_ms is the timeout duration in milliseconds. */ + unsigned timeout_duration_ms; +} DTLS1_STATE; + +struct ssl_st { + /* method is the method table corresponding to the current protocol (DTLS or + * TLS). */ + const SSL_PROTOCOL_METHOD *method; + + /* version is the protocol version. */ + int version; + + /* max_version is the maximum acceptable protocol version. Note this version + * is normalized in DTLS. */ + uint16_t max_version; + + /* min_version is the minimum acceptable protocol version. Note this version + * is normalized in DTLS. */ + uint16_t min_version; + + uint16_t max_send_fragment; + + /* There are 2 BIO's even though they are normally both the same. This is so + * data can be read and written to different handlers */ + + BIO *rbio; /* used by SSL_read */ + BIO *wbio; /* used by SSL_write */ + + int (*handshake_func)(SSL_HANDSHAKE *hs); + + BUF_MEM *init_buf; /* buffer used during init */ + + /* init_msg is a pointer to the current handshake message body. */ + const uint8_t *init_msg; + /* init_num is the length of the current handshake message body. */ + uint32_t init_num; + + struct ssl3_state_st *s3; /* SSLv3 variables */ + struct dtls1_state_st *d1; /* DTLSv1 variables */ + + /* callback that allows applications to peek at protocol messages */ + void (*msg_callback)(int write_p, int version, int content_type, + const void *buf, size_t len, SSL *ssl, void *arg); + void *msg_callback_arg; + + X509_VERIFY_PARAM *param; + + /* crypto */ + struct ssl_cipher_preference_list_st *cipher_list; + + /* session info */ + + /* client cert? */ + /* This is used to hold the server certificate used */ + struct cert_st /* CERT */ *cert; + + /* This holds a variable that indicates what we were doing when a 0 or -1 is + * returned. This is needed for non-blocking IO so we know what request + * needs re-doing when in SSL_accept or SSL_connect */ + int rwstate; + + /* initial_timeout_duration_ms is the default DTLS timeout duration in + * milliseconds. It's used to initialize the timer any time it's restarted. */ + unsigned initial_timeout_duration_ms; + + /* session is the configured session to be offered by the client. This session + * is immutable. */ + SSL_SESSION *session; + + int (*verify_callback)(int ok, + X509_STORE_CTX *ctx); /* fail if callback returns 0 */ + + void (*info_callback)(const SSL *ssl, int type, int value); + + /* Server-only: psk_identity_hint is the identity hint to send in + * PSK-based key exchanges. */ + char *psk_identity_hint; + + unsigned int (*psk_client_callback)(SSL *ssl, const char *hint, + char *identity, + unsigned int max_identity_len, + uint8_t *psk, unsigned int max_psk_len); + unsigned int (*psk_server_callback)(SSL *ssl, const char *identity, + uint8_t *psk, unsigned int max_psk_len); + + SSL_CTX *ctx; + + /* extra application data */ + CRYPTO_EX_DATA ex_data; + + /* for server side, keep the list of CA_dn we can use */ + STACK_OF(CRYPTO_BUFFER) *client_CA; + + /* cached_x509_client_CA is a cache of parsed versions of the elements of + * |client_CA|. */ + STACK_OF(X509_NAME) *cached_x509_client_CA; + + uint32_t options; /* protocol behaviour */ + uint32_t mode; /* API behaviour */ + uint32_t max_cert_list; + char *tlsext_hostname; + size_t supported_group_list_len; + uint16_t *supported_group_list; /* our list */ + + /* session_ctx is the |SSL_CTX| used for the session cache and related + * settings. */ + SSL_CTX *session_ctx; + + /* srtp_profiles is the list of configured SRTP protection profiles for + * DTLS-SRTP. */ + STACK_OF(SRTP_PROTECTION_PROFILE) *srtp_profiles; + + /* srtp_profile is the selected SRTP protection profile for + * DTLS-SRTP. */ + const SRTP_PROTECTION_PROFILE *srtp_profile; + + /* The client's Channel ID private key. */ + EVP_PKEY *tlsext_channel_id_private; + + /* For a client, this contains the list of supported protocols in wire + * format. */ + uint8_t *alpn_client_proto_list; + unsigned alpn_client_proto_list_len; + + /* renegotiate_mode controls how peer renegotiation attempts are handled. */ + enum ssl_renegotiate_mode_t renegotiate_mode; + + /* verify_mode is a bitmask of |SSL_VERIFY_*| values. */ + uint8_t verify_mode; + + /* server is true iff the this SSL* is the server half. Note: before the SSL* + * is initialized by either SSL_set_accept_state or SSL_set_connect_state, + * the side is not determined. In this state, server is always false. */ + unsigned server:1; + + /* quiet_shutdown is true if the connection should not send a close_notify on + * shutdown. */ + unsigned quiet_shutdown:1; + + /* Enable signed certificate time stamps. Currently client only. */ + unsigned signed_cert_timestamps_enabled:1; + + /* ocsp_stapling_enabled is only used by client connections and indicates + * whether OCSP stapling will be requested. */ + unsigned ocsp_stapling_enabled:1; + + /* tlsext_channel_id_enabled is copied from the |SSL_CTX|. For a server, + * means that we'll accept Channel IDs from clients. For a client, means that + * we'll advertise support. */ + unsigned tlsext_channel_id_enabled:1; + + /* retain_only_sha256_of_client_certs is true if we should compute the SHA256 + * hash of the peer's certificate and then discard it to save memory and + * session space. Only effective on the server side. */ + unsigned retain_only_sha256_of_client_certs:1; +}; + +/* From draft-ietf-tls-tls13-18, used in determining PSK modes. */ +#define SSL_PSK_KE 0x0 +#define SSL_PSK_DHE_KE 0x1 + +/* From draft-ietf-tls-tls13-16, used in determining whether to respond with a + * KeyUpdate. */ +#define SSL_KEY_UPDATE_NOT_REQUESTED 0 +#define SSL_KEY_UPDATE_REQUESTED 1 + +CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method); +CERT *ssl_cert_dup(CERT *cert); +void ssl_cert_clear_certs(CERT *c); +void ssl_cert_free(CERT *c); +int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer); +int ssl_is_key_type_supported(int key_type); +/* ssl_compare_public_and_private_key returns one if |pubkey| is the public + * counterpart to |privkey|. Otherwise it returns zero and pushes a helpful + * message on the error queue. */ +int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey, + const EVP_PKEY *privkey); +int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey); +int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server); +int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session); + +/* ssl_session_new returns a newly-allocated blank |SSL_SESSION| or NULL on + * error. */ +SSL_SESSION *ssl_session_new(const SSL_X509_METHOD *x509_method); + +/* SSL_SESSION_parse parses an |SSL_SESSION| from |cbs| and advances |cbs| over + * the parsed data. */ +SSL_SESSION *SSL_SESSION_parse(CBS *cbs, const SSL_X509_METHOD *x509_method, + CRYPTO_BUFFER_POOL *pool); + +/* ssl_session_is_context_valid returns one if |session|'s session ID context + * matches the one set on |ssl| and zero otherwise. */ +int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session); + +/* ssl_session_is_time_valid returns one if |session| is still valid and zero if + * it has expired. */ +int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session); + +/* ssl_session_is_resumable returns one if |session| is resumable for |hs| and + * zero otherwise. */ +int ssl_session_is_resumable(const SSL_HANDSHAKE *hs, + const SSL_SESSION *session); + +/* SSL_SESSION_get_digest returns the digest used in |session|. If the digest is + * invalid, it returns NULL. */ +const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session, + const SSL *ssl); + +void ssl_set_session(SSL *ssl, SSL_SESSION *session); + +enum ssl_session_result_t { + ssl_session_success, + ssl_session_error, + ssl_session_retry, + ssl_session_ticket_retry, +}; + +/* ssl_get_prev_session looks up the previous session based on |client_hello|. + * On success, it sets |*out_session| to the session or NULL if none was found. + * If the session could not be looked up synchronously, it returns + * |ssl_session_retry| and should be called again. If a ticket could not be + * decrypted immediately it returns |ssl_session_ticket_retry| and should also + * be called again. Otherwise, it returns |ssl_session_error|. */ +enum ssl_session_result_t ssl_get_prev_session( + SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported, + int *out_renew_ticket, const SSL_CLIENT_HELLO *client_hello); + +/* The following flags determine which parts of the session are duplicated. */ +#define SSL_SESSION_DUP_AUTH_ONLY 0x0 +#define SSL_SESSION_INCLUDE_TICKET 0x1 +#define SSL_SESSION_INCLUDE_NONAUTH 0x2 +#define SSL_SESSION_DUP_ALL \ + (SSL_SESSION_INCLUDE_TICKET | SSL_SESSION_INCLUDE_NONAUTH) + +/* SSL_SESSION_dup returns a newly-allocated |SSL_SESSION| with a copy of the + * fields in |session| or NULL on error. The new session is non-resumable and + * must be explicitly marked resumable once it has been filled in. */ +OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, + int dup_flags); + +/* ssl_session_rebase_time updates |session|'s start time to the current time, + * adjusting the timeout so the expiration time is unchanged. */ +void ssl_session_rebase_time(SSL *ssl, SSL_SESSION *session); + +/* ssl_session_renew_timeout calls |ssl_session_rebase_time| and renews + * |session|'s timeout to |timeout| (measured from the current time). The + * renewal is clamped to the session's auth_timeout. */ +void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session, + uint32_t timeout); + +void ssl_cipher_preference_list_free( + struct ssl_cipher_preference_list_st *cipher_list); + +/* ssl_get_cipher_preferences returns the cipher preference list for TLS 1.2 and + * below. */ +const struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences( + const SSL *ssl); + +void ssl_update_cache(SSL_HANDSHAKE *hs, int mode); + +int ssl3_get_finished(SSL_HANDSHAKE *hs); +int ssl3_send_alert(SSL *ssl, int level, int desc); +int ssl3_get_message(SSL *ssl); +void ssl3_get_current_message(const SSL *ssl, CBS *out); +void ssl3_release_current_message(SSL *ssl, int free_buffer); + +int ssl3_send_finished(SSL_HANDSHAKE *hs); +int ssl3_dispatch_alert(SSL *ssl); +int ssl3_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len, + int peek); +int ssl3_read_change_cipher_spec(SSL *ssl); +void ssl3_read_close_notify(SSL *ssl); +int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len); +int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len); +int ssl3_output_cert_chain(SSL *ssl); + +int ssl3_new(SSL *ssl); +void ssl3_free(SSL *ssl); +int ssl3_accept(SSL_HANDSHAKE *hs); +int ssl3_connect(SSL_HANDSHAKE *hs); + +int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); +int ssl3_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len); +int ssl3_add_message(SSL *ssl, uint8_t *msg, size_t len); +int ssl3_add_change_cipher_spec(SSL *ssl); +int ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc); +int ssl3_flush_flight(SSL *ssl); + +int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type); +int dtls1_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg, + size_t *out_len); +int dtls1_add_message(SSL *ssl, uint8_t *msg, size_t len); +int dtls1_add_change_cipher_spec(SSL *ssl); +int dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc); +int dtls1_flush_flight(SSL *ssl); + +/* ssl_add_message_cbb finishes the handshake message in |cbb| and adds it to + * the pending flight. It returns one on success and zero on error. */ +int ssl_add_message_cbb(SSL *ssl, CBB *cbb); + +/* ssl_hash_current_message incorporates the current handshake message into the + * handshake hash. It returns one on success and zero on allocation failure. */ +int ssl_hash_current_message(SSL_HANDSHAKE *hs); + +/* dtls1_get_record reads a new input record. On success, it places it in + * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if + * more data is needed. */ +int dtls1_get_record(SSL *ssl); + +int dtls1_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len, + int peek); +int dtls1_read_change_cipher_spec(SSL *ssl); +void dtls1_read_close_notify(SSL *ssl); + +int dtls1_write_app_data(SSL *ssl, const uint8_t *buf, int len); + +/* dtls1_write_record sends a record. It returns one on success and <= 0 on + * error. */ +int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len, + enum dtls1_use_epoch_t use_epoch); + +int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen); +int dtls1_retransmit_outgoing_messages(SSL *ssl); +void dtls1_clear_record_buffer(SSL *ssl); +int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr, + CBS *out_body); +int dtls1_check_timeout_num(SSL *ssl); +int dtls1_handshake_write(SSL *ssl); + +void dtls1_start_timer(SSL *ssl); +void dtls1_stop_timer(SSL *ssl); +int dtls1_is_timer_expired(SSL *ssl); +void dtls1_double_timeout(SSL *ssl); +unsigned int dtls1_min_mtu(void); + +int dtls1_new(SSL *ssl); +int dtls1_accept(SSL *ssl); +int dtls1_connect(SSL *ssl); +void dtls1_free(SSL *ssl); + +int dtls1_get_message(SSL *ssl); +void dtls1_get_current_message(const SSL *ssl, CBS *out); +void dtls1_release_current_message(SSL *ssl, int free_buffer); +int dtls1_dispatch_alert(SSL *ssl); + +int tls1_change_cipher_state(SSL_HANDSHAKE *hs, int which); +int tls1_generate_master_secret(SSL_HANDSHAKE *hs, uint8_t *out, + const uint8_t *premaster, size_t premaster_len); + +/* tls1_get_grouplist sets |*out_group_ids| and |*out_group_ids_len| to the + * locally-configured group preference list. */ +void tls1_get_grouplist(SSL *ssl, const uint16_t **out_group_ids, + size_t *out_group_ids_len); + +/* tls1_check_group_id returns one if |group_id| is consistent with + * locally-configured group preferences. */ +int tls1_check_group_id(SSL *ssl, uint16_t group_id); + +/* tls1_get_shared_group sets |*out_group_id| to the first preferred shared + * group between client and server preferences and returns one. If none may be + * found, it returns zero. */ +int tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id); + +/* tls1_set_curves converts the array of |ncurves| NIDs pointed to by |curves| + * into a newly allocated array of TLS group IDs. On success, the function + * returns one and writes the array to |*out_group_ids| and its size to + * |*out_group_ids_len|. Otherwise, it returns zero. */ +int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len, + const int *curves, size_t ncurves); + +/* tls1_set_curves_list converts the string of curves pointed to by |curves| + * into a newly allocated array of TLS group IDs. On success, the function + * returns one and writes the array to |*out_group_ids| and its size to + * |*out_group_ids_len|. Otherwise, it returns zero. */ +int tls1_set_curves_list(uint16_t **out_group_ids, size_t *out_group_ids_len, + const char *curves); + +/* ssl_add_clienthello_tlsext writes ClientHello extensions to |out|. It + * returns one on success and zero on failure. The |header_len| argument is the + * length of the ClientHello written so far and is used to compute the padding + * length. (It does not include the record header.) */ +int ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, size_t header_len); + +int ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out); +int ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs, + const SSL_CLIENT_HELLO *client_hello); +int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs); + +#define tlsext_tick_md EVP_sha256 + +/* ssl_process_ticket processes a session ticket from the client. It returns + * one of: + * |ssl_ticket_aead_success|: |*out_session| is set to the parsed session and + * |*out_renew_ticket| is set to whether the ticket should be renewed. + * |ssl_ticket_aead_ignore_ticket|: |*out_renew_ticket| is set to whether a + * fresh ticket should be sent, but the given ticket cannot be used. + * |ssl_ticket_aead_retry|: the ticket could not be immediately decrypted. + * Retry later. + * |ssl_ticket_aead_error|: an error occured that is fatal to the connection. */ +enum ssl_ticket_aead_result_t ssl_process_ticket( + SSL *ssl, SSL_SESSION **out_session, int *out_renew_ticket, + const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id, + size_t session_id_len); + +/* tls1_verify_channel_id processes the current message as a Channel ID message, + * and verifies the signature. If the key is valid, it saves the Channel ID and + * returns one. Otherwise, it returns zero. */ +int tls1_verify_channel_id(SSL_HANDSHAKE *hs); + +/* tls1_write_channel_id generates a Channel ID message and puts the output in + * |cbb|. |ssl->tlsext_channel_id_private| must already be set before calling. + * This function returns one on success and zero on error. */ +int tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb); + +/* tls1_channel_id_hash computes the hash to be signed by Channel ID and writes + * it to |out|, which must contain at least |EVP_MAX_MD_SIZE| bytes. It returns + * one on success and zero on failure. */ +int tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len); + +int tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs); + +/* ssl_do_channel_id_callback checks runs |ssl->ctx->channel_id_cb| if + * necessary. It returns one on success and zero on fatal error. Note that, on + * success, |ssl->tlsext_channel_id_private| may be unset, in which case the + * operation should be retried later. */ +int ssl_do_channel_id_callback(SSL *ssl); + +/* ssl3_can_false_start returns one if |ssl| is allowed to False Start and zero + * otherwise. */ +int ssl3_can_false_start(const SSL *ssl); + +/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the + * minimum and maximum enabled protocol versions, respectively. */ +int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version, + uint16_t *out_max_version); + +/* ssl3_protocol_version returns |ssl|'s protocol version. It is an error to + * call this function before the version is determined. */ +uint16_t ssl3_protocol_version(const SSL *ssl); + +void ssl_get_current_time(const SSL *ssl, struct OPENSSL_timeval *out_clock); + +/* ssl_reset_error_state resets state for |SSL_get_error|. */ +void ssl_reset_error_state(SSL *ssl); + + +#if defined(__cplusplus) +} /* extern C */ +#endif + +#endif /* OPENSSL_HEADER_SSL_INTERNAL_H */ diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_both.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_both.c new file mode 100644 index 000000000..8fa51e932 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_both.c @@ -0,0 +1,821 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/md5.h> +#include <openssl/nid.h> +#include <openssl/rand.h> +#include <openssl/sha.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl) { + SSL_HANDSHAKE *hs = OPENSSL_malloc(sizeof(SSL_HANDSHAKE)); + if (hs == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return NULL; + } + OPENSSL_memset(hs, 0, sizeof(SSL_HANDSHAKE)); + hs->ssl = ssl; + hs->wait = ssl_hs_ok; + hs->state = SSL_ST_INIT; + if (!SSL_TRANSCRIPT_init(&hs->transcript)) { + ssl_handshake_free(hs); + return NULL; + } + return hs; +} + +void ssl_handshake_free(SSL_HANDSHAKE *hs) { + if (hs == NULL) { + return; + } + + OPENSSL_cleanse(hs->secret, sizeof(hs->secret)); + OPENSSL_cleanse(hs->client_handshake_secret, + sizeof(hs->client_handshake_secret)); + OPENSSL_cleanse(hs->server_handshake_secret, + sizeof(hs->server_handshake_secret)); + OPENSSL_cleanse(hs->client_traffic_secret_0, + sizeof(hs->client_traffic_secret_0)); + OPENSSL_cleanse(hs->server_traffic_secret_0, + sizeof(hs->server_traffic_secret_0)); + SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx); + SSL_TRANSCRIPT_cleanup(&hs->transcript); + OPENSSL_free(hs->cookie); + OPENSSL_free(hs->key_share_bytes); + OPENSSL_free(hs->public_key); + SSL_SESSION_free(hs->new_session); + OPENSSL_free(hs->peer_sigalgs); + OPENSSL_free(hs->peer_supported_group_list); + OPENSSL_free(hs->peer_key); + OPENSSL_free(hs->server_params); + OPENSSL_free(hs->peer_psk_identity_hint); + sk_CRYPTO_BUFFER_pop_free(hs->ca_names, CRYPTO_BUFFER_free); + hs->ssl->ctx->x509_method->hs_flush_cached_ca_names(hs); + OPENSSL_free(hs->certificate_types); + + if (hs->key_block != NULL) { + OPENSSL_cleanse(hs->key_block, hs->key_block_len); + OPENSSL_free(hs->key_block); + } + + OPENSSL_free(hs->hostname); + EVP_PKEY_free(hs->peer_pubkey); + OPENSSL_free(hs); +} + +int ssl_check_message_type(SSL *ssl, int type) { + if (ssl->s3->tmp.message_type != type) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); + ERR_add_error_dataf("got type %d, wanted type %d", + ssl->s3->tmp.message_type, type); + return 0; + } + + return 1; +} + +static int add_record_to_flight(SSL *ssl, uint8_t type, const uint8_t *in, + size_t in_len) { + /* We'll never add a flight while in the process of writing it out. */ + assert(ssl->s3->pending_flight_offset == 0); + + if (ssl->s3->pending_flight == NULL) { + ssl->s3->pending_flight = BUF_MEM_new(); + if (ssl->s3->pending_flight == NULL) { + return 0; + } + } + + size_t max_out = in_len + SSL_max_seal_overhead(ssl); + size_t new_cap = ssl->s3->pending_flight->length + max_out; + if (max_out < in_len || new_cap < max_out) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + + size_t len; + if (!BUF_MEM_reserve(ssl->s3->pending_flight, new_cap) || + !tls_seal_record(ssl, (uint8_t *)ssl->s3->pending_flight->data + + ssl->s3->pending_flight->length, + &len, max_out, type, in, in_len)) { + return 0; + } + + ssl->s3->pending_flight->length += len; + return 1; +} + +int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { + /* Pick a modest size hint to save most of the |realloc| calls. */ + if (!CBB_init(cbb, 64) || + !CBB_add_u8(cbb, type) || + !CBB_add_u24_length_prefixed(cbb, body)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(cbb); + return 0; + } + + return 1; +} + +int ssl3_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg, + size_t *out_len) { + if (!CBB_finish(cbb, out_msg, out_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + return 1; +} + +int ssl3_add_message(SSL *ssl, uint8_t *msg, size_t len) { + /* Add the message to the current flight, splitting into several records if + * needed. */ + int ret = 0; + size_t added = 0; + do { + size_t todo = len - added; + if (todo > ssl->max_send_fragment) { + todo = ssl->max_send_fragment; + } + + if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, msg + added, todo)) { + goto err; + } + added += todo; + } while (added < len); + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, msg, len); + /* TODO(svaldez): Move this up a layer to fix abstraction for SSL_TRANSCRIPT + * on hs. */ + if (ssl->s3->hs != NULL && + !SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript, msg, len)) { + goto err; + } + ret = 1; + +err: + OPENSSL_free(msg); + return ret; +} + +int ssl3_add_change_cipher_spec(SSL *ssl) { + static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS}; + + if (!add_record_to_flight(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec, + sizeof(kChangeCipherSpec))) { + return 0; + } + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC, + kChangeCipherSpec, sizeof(kChangeCipherSpec)); + return 1; +} + +int ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc) { + uint8_t alert[2] = {level, desc}; + if (!add_record_to_flight(ssl, SSL3_RT_ALERT, alert, sizeof(alert))) { + return 0; + } + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, alert, sizeof(alert)); + ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, ((int)level << 8) | desc); + return 1; +} + +int ssl_add_message_cbb(SSL *ssl, CBB *cbb) { + uint8_t *msg; + size_t len; + if (!ssl->method->finish_message(ssl, cbb, &msg, &len) || + !ssl->method->add_message(ssl, msg, len)) { + return 0; + } + + return 1; +} + +int ssl3_flush_flight(SSL *ssl) { + if (ssl->s3->pending_flight == NULL) { + return 1; + } + + if (ssl->s3->pending_flight->length > 0xffffffff || + ssl->s3->pending_flight->length > INT_MAX) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* The handshake flight buffer is mutually exclusive with application data. + * + * TODO(davidben): This will not be true when closure alerts use this. */ + if (ssl_write_buffer_is_pending(ssl)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* Write the pending flight. */ + while (ssl->s3->pending_flight_offset < ssl->s3->pending_flight->length) { + int ret = BIO_write( + ssl->wbio, + ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset, + ssl->s3->pending_flight->length - ssl->s3->pending_flight_offset); + if (ret <= 0) { + ssl->rwstate = SSL_WRITING; + return ret; + } + + ssl->s3->pending_flight_offset += ret; + } + + if (BIO_flush(ssl->wbio) <= 0) { + ssl->rwstate = SSL_WRITING; + return -1; + } + + BUF_MEM_free(ssl->s3->pending_flight); + ssl->s3->pending_flight = NULL; + ssl->s3->pending_flight_offset = 0; + return 1; +} + +int ssl3_send_finished(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + const SSL_SESSION *session = SSL_get_session(ssl); + + uint8_t finished[EVP_MAX_MD_SIZE]; + size_t finished_len; + if (!SSL_TRANSCRIPT_finish_mac(&hs->transcript, finished, &finished_len, + session, ssl->server, + ssl3_protocol_version(ssl))) { + return 0; + } + + /* Log the master secret, if logging is enabled. */ + if (!ssl_log_secret(ssl, "CLIENT_RANDOM", + session->master_key, + session->master_key_length)) { + return 0; + } + + /* Copy the Finished so we can use it for renegotiation checks. */ + if (ssl->version != SSL3_VERSION) { + if (finished_len > sizeof(ssl->s3->previous_client_finished) || + finished_len > sizeof(ssl->s3->previous_server_finished)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + if (ssl->server) { + OPENSSL_memcpy(ssl->s3->previous_server_finished, finished, finished_len); + ssl->s3->previous_server_finished_len = finished_len; + } else { + OPENSSL_memcpy(ssl->s3->previous_client_finished, finished, finished_len); + ssl->s3->previous_client_finished_len = finished_len; + } + } + + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) || + !CBB_add_bytes(&body, finished, finished_len) || + !ssl_add_message_cbb(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return -1; + } + + return 1; +} + +int ssl3_get_finished(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED)) { + return -1; + } + + /* Snapshot the finished hash before incorporating the new message. */ + uint8_t finished[EVP_MAX_MD_SIZE]; + size_t finished_len; + if (!SSL_TRANSCRIPT_finish_mac(&hs->transcript, finished, &finished_len, + SSL_get_session(ssl), !ssl->server, + ssl3_protocol_version(ssl)) || + !ssl_hash_current_message(hs)) { + return -1; + } + + int finished_ok = ssl->init_num == finished_len && + CRYPTO_memcmp(ssl->init_msg, finished, finished_len) == 0; +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + finished_ok = 1; +#endif + if (!finished_ok) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED); + return -1; + } + + /* Copy the Finished so we can use it for renegotiation checks. */ + if (ssl->version != SSL3_VERSION) { + if (finished_len > sizeof(ssl->s3->previous_client_finished) || + finished_len > sizeof(ssl->s3->previous_server_finished)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + if (ssl->server) { + OPENSSL_memcpy(ssl->s3->previous_client_finished, finished, finished_len); + ssl->s3->previous_client_finished_len = finished_len; + } else { + OPENSSL_memcpy(ssl->s3->previous_server_finished, finished, finished_len); + ssl->s3->previous_server_finished_len = finished_len; + } + } + + return 1; +} + +int ssl3_output_cert_chain(SSL *ssl) { + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) || + !ssl_add_cert_chain(ssl, &body) || + !ssl_add_message_cbb(ssl, &cbb)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + CBB_cleanup(&cbb); + return 0; + } + + return 1; +} + +size_t ssl_max_handshake_message_len(const SSL *ssl) { + /* kMaxMessageLen is the default maximum message size for handshakes which do + * not accept peer certificate chains. */ + static const size_t kMaxMessageLen = 16384; + + if (SSL_in_init(ssl)) { + if ((!ssl->server || (ssl->verify_mode & SSL_VERIFY_PEER)) && + kMaxMessageLen < ssl->max_cert_list) { + return ssl->max_cert_list; + } + return kMaxMessageLen; + } + + if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + /* In TLS 1.2 and below, the largest acceptable post-handshake message is + * a HelloRequest. */ + return 0; + } + + if (ssl->server) { + /* The largest acceptable post-handshake message for a server is a + * KeyUpdate. We will never initiate post-handshake auth. */ + return 1; + } + + /* Clients must accept NewSessionTicket and CertificateRequest, so allow the + * default size. */ + return kMaxMessageLen; +} + +static int extend_handshake_buffer(SSL *ssl, size_t length) { + if (!BUF_MEM_reserve(ssl->init_buf, length)) { + return -1; + } + while (ssl->init_buf->length < length) { + int ret = ssl3_read_handshake_bytes( + ssl, (uint8_t *)ssl->init_buf->data + ssl->init_buf->length, + length - ssl->init_buf->length); + if (ret <= 0) { + return ret; + } + ssl->init_buf->length += (size_t)ret; + } + return 1; +} + +static int read_v2_client_hello(SSL *ssl) { + /* Read the first 5 bytes, the size of the TLS record header. This is + * sufficient to detect a V2ClientHello and ensures that we never read beyond + * the first record. */ + int ret = ssl_read_buffer_extend_to(ssl, SSL3_RT_HEADER_LENGTH); + if (ret <= 0) { + return ret; + } + const uint8_t *p = ssl_read_buffer(ssl); + + /* Some dedicated error codes for protocol mixups should the application wish + * to interpret them differently. (These do not overlap with ClientHello or + * V2ClientHello.) */ + if (strncmp("GET ", (const char *)p, 4) == 0 || + strncmp("POST ", (const char *)p, 5) == 0 || + strncmp("HEAD ", (const char *)p, 5) == 0 || + strncmp("PUT ", (const char *)p, 4) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_HTTP_REQUEST); + return -1; + } + if (strncmp("CONNE", (const char *)p, 5) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_HTTPS_PROXY_REQUEST); + return -1; + } + + if ((p[0] & 0x80) == 0 || p[2] != SSL2_MT_CLIENT_HELLO || + p[3] != SSL3_VERSION_MAJOR) { + /* Not a V2ClientHello. */ + return 1; + } + + /* Determine the length of the V2ClientHello. */ + size_t msg_length = ((p[0] & 0x7f) << 8) | p[1]; + if (msg_length > (1024 * 4)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_TOO_LARGE); + return -1; + } + if (msg_length < SSL3_RT_HEADER_LENGTH - 2) { + /* Reject lengths that are too short early. We have already read + * |SSL3_RT_HEADER_LENGTH| bytes, so we should not attempt to process an + * (invalid) V2ClientHello which would be shorter than that. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_RECORD_LENGTH_MISMATCH); + return -1; + } + + /* Read the remainder of the V2ClientHello. */ + ret = ssl_read_buffer_extend_to(ssl, 2 + msg_length); + if (ret <= 0) { + return ret; + } + + CBS v2_client_hello; + CBS_init(&v2_client_hello, ssl_read_buffer(ssl) + 2, msg_length); + + /* The V2ClientHello without the length is incorporated into the handshake + * hash. This is only ever called at the start of the handshake, so hs is + * guaranteed to be non-NULL. */ + if (!SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript, + CBS_data(&v2_client_hello), + CBS_len(&v2_client_hello))) { + return -1; + } + + ssl_do_msg_callback(ssl, 0 /* read */, 0 /* V2ClientHello */, + CBS_data(&v2_client_hello), CBS_len(&v2_client_hello)); + + uint8_t msg_type; + uint16_t version, cipher_spec_length, session_id_length, challenge_length; + CBS cipher_specs, session_id, challenge; + if (!CBS_get_u8(&v2_client_hello, &msg_type) || + !CBS_get_u16(&v2_client_hello, &version) || + !CBS_get_u16(&v2_client_hello, &cipher_spec_length) || + !CBS_get_u16(&v2_client_hello, &session_id_length) || + !CBS_get_u16(&v2_client_hello, &challenge_length) || + !CBS_get_bytes(&v2_client_hello, &cipher_specs, cipher_spec_length) || + !CBS_get_bytes(&v2_client_hello, &session_id, session_id_length) || + !CBS_get_bytes(&v2_client_hello, &challenge, challenge_length) || + CBS_len(&v2_client_hello) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return -1; + } + + /* msg_type has already been checked. */ + assert(msg_type == SSL2_MT_CLIENT_HELLO); + + /* The client_random is the V2ClientHello challenge. Truncate or + * left-pad with zeros as needed. */ + size_t rand_len = CBS_len(&challenge); + if (rand_len > SSL3_RANDOM_SIZE) { + rand_len = SSL3_RANDOM_SIZE; + } + uint8_t random[SSL3_RANDOM_SIZE]; + OPENSSL_memset(random, 0, SSL3_RANDOM_SIZE); + OPENSSL_memcpy(random + (SSL3_RANDOM_SIZE - rand_len), CBS_data(&challenge), + rand_len); + + /* Write out an equivalent SSLv3 ClientHello. */ + size_t max_v3_client_hello = SSL3_HM_HEADER_LENGTH + 2 /* version */ + + SSL3_RANDOM_SIZE + 1 /* session ID length */ + + 2 /* cipher list length */ + + CBS_len(&cipher_specs) / 3 * 2 + + 1 /* compression length */ + 1 /* compression */; + CBB client_hello, hello_body, cipher_suites; + CBB_zero(&client_hello); + if (!BUF_MEM_reserve(ssl->init_buf, max_v3_client_hello) || + !CBB_init_fixed(&client_hello, (uint8_t *)ssl->init_buf->data, + ssl->init_buf->max) || + !CBB_add_u8(&client_hello, SSL3_MT_CLIENT_HELLO) || + !CBB_add_u24_length_prefixed(&client_hello, &hello_body) || + !CBB_add_u16(&hello_body, version) || + !CBB_add_bytes(&hello_body, random, SSL3_RANDOM_SIZE) || + /* No session id. */ + !CBB_add_u8(&hello_body, 0) || + !CBB_add_u16_length_prefixed(&hello_body, &cipher_suites)) { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return -1; + } + + /* Copy the cipher suites. */ + while (CBS_len(&cipher_specs) > 0) { + uint32_t cipher_spec; + if (!CBS_get_u24(&cipher_specs, &cipher_spec)) { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return -1; + } + + /* Skip SSLv2 ciphers. */ + if ((cipher_spec & 0xff0000) != 0) { + continue; + } + if (!CBB_add_u16(&cipher_suites, cipher_spec)) { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + } + + /* Add the null compression scheme and finish. */ + if (!CBB_add_u8(&hello_body, 1) || !CBB_add_u8(&hello_body, 0) || + !CBB_finish(&client_hello, NULL, &ssl->init_buf->length)) { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* Consume and discard the V2ClientHello. */ + ssl_read_buffer_consume(ssl, 2 + msg_length); + ssl_read_buffer_discard(ssl); + + ssl->s3->is_v2_hello = 1; + /* This is the first message, so hs must be non-NULL. */ + ssl->s3->hs->v2_clienthello = 1; + return 1; +} + +int ssl3_get_message(SSL *ssl) { + /* Re-create the handshake buffer if needed. */ + if (ssl->init_buf == NULL) { + ssl->init_buf = BUF_MEM_new(); + if (ssl->init_buf == NULL) { + return -1; + } + } + + if (ssl->server && !ssl->s3->v2_hello_done) { + /* Bypass the record layer for the first message to handle V2ClientHello. */ + int ret = read_v2_client_hello(ssl); + if (ret <= 0) { + return ret; + } + ssl->s3->v2_hello_done = 1; + } + + if (ssl->s3->tmp.reuse_message) { + /* There must be a current message. */ + assert(ssl->init_msg != NULL); + ssl->s3->tmp.reuse_message = 0; + } else { + ssl3_release_current_message(ssl, 0 /* don't free buffer */); + } + + /* Read the message header, if we haven't yet. */ + int ret = extend_handshake_buffer(ssl, SSL3_HM_HEADER_LENGTH); + if (ret <= 0) { + return ret; + } + + /* Parse out the length. Cap it so the peer cannot force us to buffer up to + * 2^24 bytes. */ + const uint8_t *p = (uint8_t *)ssl->init_buf->data; + size_t msg_len = (((uint32_t)p[1]) << 16) | (((uint32_t)p[2]) << 8) | p[3]; + if (msg_len > ssl_max_handshake_message_len(ssl)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE); + return -1; + } + + /* Read the message body, if we haven't yet. */ + ret = extend_handshake_buffer(ssl, SSL3_HM_HEADER_LENGTH + msg_len); + if (ret <= 0) { + return ret; + } + + /* We have now received a complete message. */ + ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE, ssl->init_buf->data, + ssl->init_buf->length); + + ssl->s3->tmp.message_type = ((const uint8_t *)ssl->init_buf->data)[0]; + ssl->init_msg = (uint8_t*)ssl->init_buf->data + SSL3_HM_HEADER_LENGTH; + ssl->init_num = ssl->init_buf->length - SSL3_HM_HEADER_LENGTH; + return 1; +} + +void ssl3_get_current_message(const SSL *ssl, CBS *out) { + CBS_init(out, (uint8_t *)ssl->init_buf->data, ssl->init_buf->length); +} + +int ssl_hash_current_message(SSL_HANDSHAKE *hs) { + /* V2ClientHellos are hashed implicitly. */ + if (hs->ssl->s3->is_v2_hello) { + return 1; + } + + CBS cbs; + hs->ssl->method->get_current_message(hs->ssl, &cbs); + return SSL_TRANSCRIPT_update(&hs->transcript, CBS_data(&cbs), CBS_len(&cbs)); +} + +void ssl3_release_current_message(SSL *ssl, int free_buffer) { + if (ssl->init_msg != NULL) { + /* |init_buf| never contains data beyond the current message. */ + assert(SSL3_HM_HEADER_LENGTH + ssl->init_num == ssl->init_buf->length); + + /* Clear the current message. */ + ssl->init_msg = NULL; + ssl->init_num = 0; + ssl->init_buf->length = 0; + ssl->s3->is_v2_hello = 0; + } + + if (free_buffer) { + BUF_MEM_free(ssl->init_buf); + ssl->init_buf = NULL; + } +} + +int ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert, + const SSL_EXTENSION_TYPE *ext_types, + size_t num_ext_types, int ignore_unknown) { + /* Reset everything. */ + for (size_t i = 0; i < num_ext_types; i++) { + *ext_types[i].out_present = 0; + CBS_init(ext_types[i].out_data, NULL, 0); + } + + CBS copy = *cbs; + while (CBS_len(©) != 0) { + uint16_t type; + CBS data; + if (!CBS_get_u16(©, &type) || + !CBS_get_u16_length_prefixed(©, &data)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + const SSL_EXTENSION_TYPE *ext_type = NULL; + for (size_t i = 0; i < num_ext_types; i++) { + if (type == ext_types[i].type) { + ext_type = &ext_types[i]; + break; + } + } + + if (ext_type == NULL) { + if (ignore_unknown) { + continue; + } + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + /* Duplicate ext_types are forbidden. */ + if (*ext_type->out_present) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + *ext_type->out_present = 1; + *ext_type->out_data = data; + } + + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_lib.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_lib.c new file mode 100644 index 000000000..57a27c707 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_lib.c @@ -0,0 +1,220 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * + * Portions of the attached software ("Contribution") are developed by + * SUN MICROSYSTEMS, INC., and are contributed to the OpenSSL project. + * + * The Contribution is licensed pursuant to the OpenSSL open source + * license provided above. + * + * ECC cipher suite support in OpenSSL originally written by + * Vipul Gupta and Sumit Gupta of Sun Microsystems Laboratories. + * + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/dh.h> +#include <openssl/digest.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/nid.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +int ssl3_new(SSL *ssl) { + SSL3_STATE *s3; + + s3 = OPENSSL_malloc(sizeof *s3); + if (s3 == NULL) { + return 0; + } + OPENSSL_memset(s3, 0, sizeof *s3); + + s3->hs = ssl_handshake_new(ssl); + if (s3->hs == NULL) { + OPENSSL_free(s3); + return 0; + } + + ssl->s3 = s3; + + /* Set the version to the highest supported version. + * + * TODO(davidben): Move this field into |s3|, have it store the normalized + * protocol version, and implement this pre-negotiation quirk in |SSL_version| + * at the API boundary rather than in internal state. */ + ssl->version = TLS1_2_VERSION; + return 1; +} + +void ssl3_free(SSL *ssl) { + if (ssl == NULL || ssl->s3 == NULL) { + return; + } + + ssl_read_buffer_clear(ssl); + ssl_write_buffer_clear(ssl); + + SSL_SESSION_free(ssl->s3->established_session); + ssl_handshake_free(ssl->s3->hs); + OPENSSL_free(ssl->s3->next_proto_negotiated); + OPENSSL_free(ssl->s3->alpn_selected); + SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx); + SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx); + BUF_MEM_free(ssl->s3->pending_flight); + + OPENSSL_cleanse(ssl->s3, sizeof *ssl->s3); + OPENSSL_free(ssl->s3); + ssl->s3 = NULL; +} + +const struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences( + const SSL *ssl) { + if (ssl->cipher_list != NULL) { + return ssl->cipher_list; + } + + return ssl->ctx->cipher_list; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_pkt.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_pkt.c new file mode 100644 index 000000000..2f919caed --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/s3_pkt.c @@ -0,0 +1,489 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/rand.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len); + +/* ssl3_get_record reads a new input record. On success, it places it in + * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if + * more data is needed. */ +static int ssl3_get_record(SSL *ssl) { +again: + switch (ssl->s3->recv_shutdown) { + case ssl_shutdown_none: + break; + case ssl_shutdown_fatal_alert: + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + case ssl_shutdown_close_notify: + return 0; + } + + CBS body; + uint8_t type, alert = SSL_AD_DECODE_ERROR; + size_t consumed; + enum ssl_open_record_t open_ret = + tls_open_record(ssl, &type, &body, &consumed, &alert, + ssl_read_buffer(ssl), ssl_read_buffer_len(ssl)); + if (open_ret != ssl_open_record_partial) { + ssl_read_buffer_consume(ssl, consumed); + } + switch (open_ret) { + case ssl_open_record_partial: { + int read_ret = ssl_read_buffer_extend_to(ssl, consumed); + if (read_ret <= 0) { + return read_ret; + } + goto again; + } + + case ssl_open_record_success: + if (CBS_len(&body) > 0xffff) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return -1; + } + + SSL3_RECORD *rr = &ssl->s3->rrec; + rr->type = type; + rr->length = (uint16_t)CBS_len(&body); + rr->data = (uint8_t *)CBS_data(&body); + return 1; + + case ssl_open_record_discard: + goto again; + + case ssl_open_record_close_notify: + return 0; + + case ssl_open_record_fatal_alert: + return -1; + + case ssl_open_record_error: + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return -1; + } + + assert(0); + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; +} + +int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len) { + assert(!SSL_in_init(ssl) || SSL_in_false_start(ssl)); + + unsigned tot, n, nw; + + assert(ssl->s3->wnum <= INT_MAX); + tot = ssl->s3->wnum; + ssl->s3->wnum = 0; + + /* Ensure that if we end up with a smaller value of data to write out than + * the the original len from a write which didn't complete for non-blocking + * I/O and also somehow ended up avoiding the check for this in + * ssl3_write_pending/SSL_R_BAD_WRITE_RETRY as it must never be possible to + * end up with (len-tot) as a large number that will then promptly send + * beyond the end of the users buffer ... so we trap and report the error in + * a way the user will notice. */ + if (len < 0 || (size_t)len < tot) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_LENGTH); + return -1; + } + + n = len - tot; + for (;;) { + /* max contains the maximum number of bytes that we can put into a + * record. */ + unsigned max = ssl->max_send_fragment; + if (n > max) { + nw = max; + } else { + nw = n; + } + + int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &buf[tot], nw); + if (ret <= 0) { + ssl->s3->wnum = tot; + return ret; + } + + if (ret == (int)n || (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE)) { + return tot + ret; + } + + n -= ret; + tot += ret; + } +} + +static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *buf, + unsigned int len) { + if (ssl->s3->wpend_tot > (int)len || + (!(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) && + ssl->s3->wpend_buf != buf) || + ssl->s3->wpend_type != type) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY); + return -1; + } + + int ret = ssl_write_buffer_flush(ssl); + if (ret <= 0) { + return ret; + } + return ssl->s3->wpend_ret; +} + +/* do_ssl3_write writes an SSL record of the given type. */ +static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len) { + /* If there is still data from the previous record, flush it. */ + if (ssl_write_buffer_is_pending(ssl)) { + return ssl3_write_pending(ssl, type, buf, len); + } + + /* The handshake flight buffer is mutually exclusive with application data. + * + * TODO(davidben): This will not be true when closure alerts use this. */ + if (ssl->s3->pending_flight != NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + if (len > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + if (len == 0) { + return 0; + } + + size_t max_out = len + SSL_max_seal_overhead(ssl); + if (max_out < len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return -1; + } + uint8_t *out; + size_t ciphertext_len; + if (!ssl_write_buffer_init(ssl, &out, max_out) || + !tls_seal_record(ssl, out, &ciphertext_len, max_out, type, buf, len)) { + return -1; + } + ssl_write_buffer_set_len(ssl, ciphertext_len); + + /* memorize arguments so that ssl3_write_pending can detect bad write retries + * later */ + ssl->s3->wpend_tot = len; + ssl->s3->wpend_buf = buf; + ssl->s3->wpend_type = type; + ssl->s3->wpend_ret = len; + + /* we now just need to write the buffer */ + return ssl3_write_pending(ssl, type, buf, len); +} + +static int consume_record(SSL *ssl, uint8_t *out, int len, int peek) { + SSL3_RECORD *rr = &ssl->s3->rrec; + + if (len <= 0) { + return len; + } + + if (len > (int)rr->length) { + len = (int)rr->length; + } + + OPENSSL_memcpy(out, rr->data, len); + if (!peek) { + rr->length -= len; + rr->data += len; + if (rr->length == 0) { + /* The record has been consumed, so we may now clear the buffer. */ + ssl_read_buffer_discard(ssl); + } + } + return len; +} + +int ssl3_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len, + int peek) { + assert(!SSL_in_init(ssl)); + assert(ssl->s3->initial_handshake_complete); + *out_got_handshake = 0; + + SSL3_RECORD *rr = &ssl->s3->rrec; + + for (;;) { + /* A previous iteration may have read a partial handshake message. Do not + * allow more app data in that case. */ + int has_hs_data = ssl->init_buf != NULL && ssl->init_buf->length > 0; + + /* Get new packet if necessary. */ + if (rr->length == 0 && !has_hs_data) { + int ret = ssl3_get_record(ssl); + if (ret <= 0) { + return ret; + } + } + + if (has_hs_data || rr->type == SSL3_RT_HANDSHAKE) { + /* Post-handshake data prior to TLS 1.3 is always renegotiation, which we + * never accept as a server. Otherwise |ssl3_get_message| will send + * |SSL_R_EXCESSIVE_MESSAGE_SIZE|. */ + if (ssl->server && ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION); + return -1; + } + + /* Parse post-handshake handshake messages. */ + int ret = ssl3_get_message(ssl); + if (ret <= 0) { + return ret; + } + *out_got_handshake = 1; + return -1; + } + + if (rr->type != SSL3_RT_APPLICATION_DATA) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return -1; + } + + if (rr->length != 0) { + return consume_record(ssl, buf, len, peek); + } + + /* Discard empty records and loop again. */ + } +} + +int ssl3_read_change_cipher_spec(SSL *ssl) { + SSL3_RECORD *rr = &ssl->s3->rrec; + + if (rr->length == 0) { + int ret = ssl3_get_record(ssl); + if (ret <= 0) { + return ret; + } + } + + if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + return -1; + } + + if (rr->length != 1 || rr->data[0] != SSL3_MT_CCS) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return -1; + } + + ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, + rr->length); + + rr->length = 0; + ssl_read_buffer_discard(ssl); + return 1; +} + +void ssl3_read_close_notify(SSL *ssl) { + /* Read records until an error or close_notify. */ + while (ssl3_get_record(ssl) > 0) { + ; + } +} + +int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len) { + SSL3_RECORD *rr = &ssl->s3->rrec; + + for (;;) { + /* Get new packet if necessary. */ + if (rr->length == 0) { + int ret = ssl3_get_record(ssl); + if (ret <= 0) { + return ret; + } + } + + if (rr->type != SSL3_RT_HANDSHAKE) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return -1; + } + + if (rr->length != 0) { + return consume_record(ssl, buf, len, 0 /* consume data */); + } + + /* Discard empty records and loop again. */ + } +} + +int ssl3_send_alert(SSL *ssl, int level, int desc) { + /* It is illegal to send an alert when we've already sent a closing one. */ + if (ssl->s3->send_shutdown != ssl_shutdown_none) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + + if (level == SSL3_AL_WARNING && desc == SSL_AD_CLOSE_NOTIFY) { + ssl->s3->send_shutdown = ssl_shutdown_close_notify; + } else { + assert(level == SSL3_AL_FATAL); + ssl->s3->send_shutdown = ssl_shutdown_fatal_alert; + } + + ssl->s3->alert_dispatch = 1; + ssl->s3->send_alert[0] = level; + ssl->s3->send_alert[1] = desc; + if (!ssl_write_buffer_is_pending(ssl)) { + /* Nothing is being written out, so the alert may be dispatched + * immediately. */ + return ssl->method->dispatch_alert(ssl); + } + + /* The alert will be dispatched later. */ + return -1; +} + +int ssl3_dispatch_alert(SSL *ssl) { + int ret = do_ssl3_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2); + if (ret <= 0) { + return ret; + } + ssl->s3->alert_dispatch = 0; + + /* If the alert is fatal, flush the BIO now. */ + if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) { + BIO_flush(ssl->wbio); + } + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, ssl->s3->send_alert, + 2); + + int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1]; + ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, alert); + + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_aead_ctx.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_aead_ctx.c new file mode 100644 index 000000000..1af4a5ab1 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_aead_ctx.c @@ -0,0 +1,336 @@ +/* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/aead.h> +#include <openssl/err.h> +#include <openssl/rand.h> +#include <openssl/type_check.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +SSL_AEAD_CTX *SSL_AEAD_CTX_new(enum evp_aead_direction_t direction, + uint16_t version, const SSL_CIPHER *cipher, + const uint8_t *enc_key, size_t enc_key_len, + const uint8_t *mac_key, size_t mac_key_len, + const uint8_t *fixed_iv, size_t fixed_iv_len) { + const EVP_AEAD *aead; + size_t expected_mac_key_len, expected_fixed_iv_len; + if (!ssl_cipher_get_evp_aead(&aead, &expected_mac_key_len, + &expected_fixed_iv_len, cipher, version) || + /* Ensure the caller returned correct key sizes. */ + expected_fixed_iv_len != fixed_iv_len || + expected_mac_key_len != mac_key_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + uint8_t merged_key[EVP_AEAD_MAX_KEY_LENGTH]; + if (mac_key_len > 0) { + /* This is a "stateful" AEAD (for compatibility with pre-AEAD cipher + * suites). */ + if (mac_key_len + enc_key_len + fixed_iv_len > sizeof(merged_key)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + OPENSSL_memcpy(merged_key, mac_key, mac_key_len); + OPENSSL_memcpy(merged_key + mac_key_len, enc_key, enc_key_len); + OPENSSL_memcpy(merged_key + mac_key_len + enc_key_len, fixed_iv, + fixed_iv_len); + enc_key = merged_key; + enc_key_len += mac_key_len; + enc_key_len += fixed_iv_len; + } + + SSL_AEAD_CTX *aead_ctx = OPENSSL_malloc(sizeof(SSL_AEAD_CTX)); + if (aead_ctx == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return NULL; + } + OPENSSL_memset(aead_ctx, 0, sizeof(SSL_AEAD_CTX)); + aead_ctx->cipher = cipher; + aead_ctx->version = version; + + if (!EVP_AEAD_CTX_init_with_direction( + &aead_ctx->ctx, aead, enc_key, enc_key_len, + EVP_AEAD_DEFAULT_TAG_LENGTH, direction)) { + OPENSSL_free(aead_ctx); + return NULL; + } + + assert(EVP_AEAD_nonce_length(aead) <= EVP_AEAD_MAX_NONCE_LENGTH); + OPENSSL_COMPILE_ASSERT(EVP_AEAD_MAX_NONCE_LENGTH < 256, + variable_nonce_len_doesnt_fit_in_uint8_t); + aead_ctx->variable_nonce_len = (uint8_t)EVP_AEAD_nonce_length(aead); + if (mac_key_len == 0) { + assert(fixed_iv_len <= sizeof(aead_ctx->fixed_nonce)); + OPENSSL_memcpy(aead_ctx->fixed_nonce, fixed_iv, fixed_iv_len); + aead_ctx->fixed_nonce_len = fixed_iv_len; + + if (cipher->algorithm_enc & SSL_CHACHA20POLY1305) { + /* The fixed nonce into the actual nonce (the sequence number). */ + aead_ctx->xor_fixed_nonce = 1; + aead_ctx->variable_nonce_len = 8; + } else { + /* The fixed IV is prepended to the nonce. */ + assert(fixed_iv_len <= aead_ctx->variable_nonce_len); + aead_ctx->variable_nonce_len -= fixed_iv_len; + } + + /* AES-GCM uses an explicit nonce. */ + if (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) { + aead_ctx->variable_nonce_included_in_record = 1; + } + + /* The TLS 1.3 construction XORs the fixed nonce into the sequence number + * and omits the additional data. */ + if (version >= TLS1_3_VERSION) { + aead_ctx->xor_fixed_nonce = 1; + aead_ctx->variable_nonce_len = 8; + aead_ctx->variable_nonce_included_in_record = 0; + aead_ctx->omit_ad = 1; + assert(fixed_iv_len >= aead_ctx->variable_nonce_len); + } + } else { + assert(version < TLS1_3_VERSION); + aead_ctx->variable_nonce_included_in_record = 1; + aead_ctx->random_variable_nonce = 1; + aead_ctx->omit_length_in_ad = 1; + aead_ctx->omit_version_in_ad = (version == SSL3_VERSION); + } + + return aead_ctx; +} + +void SSL_AEAD_CTX_free(SSL_AEAD_CTX *aead) { + if (aead == NULL) { + return; + } + EVP_AEAD_CTX_cleanup(&aead->ctx); + OPENSSL_free(aead); +} + +size_t SSL_AEAD_CTX_explicit_nonce_len(const SSL_AEAD_CTX *aead) { +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + aead = NULL; +#endif + + if (aead != NULL && aead->variable_nonce_included_in_record) { + return aead->variable_nonce_len; + } + return 0; +} + +size_t SSL_AEAD_CTX_max_overhead(const SSL_AEAD_CTX *aead) { +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + aead = NULL; +#endif + + if (aead == NULL) { + return 0; + } + return EVP_AEAD_max_overhead(aead->ctx.aead) + + SSL_AEAD_CTX_explicit_nonce_len(aead); +} + +/* ssl_aead_ctx_get_ad writes the additional data for |aead| into |out| and + * returns the number of bytes written. */ +static size_t ssl_aead_ctx_get_ad(SSL_AEAD_CTX *aead, uint8_t out[13], + uint8_t type, uint16_t wire_version, + const uint8_t seqnum[8], + size_t plaintext_len) { + if (aead->omit_ad) { + return 0; + } + + OPENSSL_memcpy(out, seqnum, 8); + size_t len = 8; + out[len++] = type; + if (!aead->omit_version_in_ad) { + out[len++] = (uint8_t)(wire_version >> 8); + out[len++] = (uint8_t)wire_version; + } + if (!aead->omit_length_in_ad) { + out[len++] = (uint8_t)(plaintext_len >> 8); + out[len++] = (uint8_t)plaintext_len; + } + return len; +} + +int SSL_AEAD_CTX_open(SSL_AEAD_CTX *aead, CBS *out, uint8_t type, + uint16_t wire_version, const uint8_t seqnum[8], + uint8_t *in, size_t in_len) { +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + aead = NULL; +#endif + + if (aead == NULL) { + /* Handle the initial NULL cipher. */ + CBS_init(out, in, in_len); + return 1; + } + + /* TLS 1.2 AEADs include the length in the AD and are assumed to have fixed + * overhead. Otherwise the parameter is unused. */ + size_t plaintext_len = 0; + if (!aead->omit_length_in_ad) { + size_t overhead = SSL_AEAD_CTX_max_overhead(aead); + if (in_len < overhead) { + /* Publicly invalid. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH); + return 0; + } + plaintext_len = in_len - overhead; + } + uint8_t ad[13]; + size_t ad_len = ssl_aead_ctx_get_ad(aead, ad, type, wire_version, seqnum, + plaintext_len); + + /* Assemble the nonce. */ + uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH]; + size_t nonce_len = 0; + + /* Prepend the fixed nonce, or left-pad with zeros if XORing. */ + if (aead->xor_fixed_nonce) { + nonce_len = aead->fixed_nonce_len - aead->variable_nonce_len; + OPENSSL_memset(nonce, 0, nonce_len); + } else { + OPENSSL_memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len); + nonce_len += aead->fixed_nonce_len; + } + + /* Add the variable nonce. */ + if (aead->variable_nonce_included_in_record) { + if (in_len < aead->variable_nonce_len) { + /* Publicly invalid. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_PACKET_LENGTH); + return 0; + } + OPENSSL_memcpy(nonce + nonce_len, in, aead->variable_nonce_len); + in += aead->variable_nonce_len; + in_len -= aead->variable_nonce_len; + } else { + assert(aead->variable_nonce_len == 8); + OPENSSL_memcpy(nonce + nonce_len, seqnum, aead->variable_nonce_len); + } + nonce_len += aead->variable_nonce_len; + + /* XOR the fixed nonce, if necessary. */ + if (aead->xor_fixed_nonce) { + assert(nonce_len == aead->fixed_nonce_len); + for (size_t i = 0; i < aead->fixed_nonce_len; i++) { + nonce[i] ^= aead->fixed_nonce[i]; + } + } + + /* Decrypt in-place. */ + size_t len; + if (!EVP_AEAD_CTX_open(&aead->ctx, in, &len, in_len, nonce, nonce_len, + in, in_len, ad, ad_len)) { + return 0; + } + CBS_init(out, in, len); + return 1; +} + +int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len, + size_t max_out, uint8_t type, uint16_t wire_version, + const uint8_t seqnum[8], const uint8_t *in, + size_t in_len) { +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + aead = NULL; +#endif + + if (aead == NULL) { + /* Handle the initial NULL cipher. */ + if (in_len > max_out) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + OPENSSL_memmove(out, in, in_len); + *out_len = in_len; + return 1; + } + + uint8_t ad[13]; + size_t ad_len = ssl_aead_ctx_get_ad(aead, ad, type, wire_version, seqnum, + in_len); + + /* Assemble the nonce. */ + uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH]; + size_t nonce_len = 0; + + /* Prepend the fixed nonce, or left-pad with zeros if XORing. */ + if (aead->xor_fixed_nonce) { + nonce_len = aead->fixed_nonce_len - aead->variable_nonce_len; + OPENSSL_memset(nonce, 0, nonce_len); + } else { + OPENSSL_memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len); + nonce_len += aead->fixed_nonce_len; + } + + /* Select the variable nonce. */ + if (aead->random_variable_nonce) { + assert(aead->variable_nonce_included_in_record); + if (!RAND_bytes(nonce + nonce_len, aead->variable_nonce_len)) { + return 0; + } + } else { + /* When sending we use the sequence number as the variable part of the + * nonce. */ + assert(aead->variable_nonce_len == 8); + OPENSSL_memcpy(nonce + nonce_len, seqnum, aead->variable_nonce_len); + } + nonce_len += aead->variable_nonce_len; + + /* Emit the variable nonce if included in the record. */ + size_t extra_len = 0; + if (aead->variable_nonce_included_in_record) { + assert(!aead->xor_fixed_nonce); + if (max_out < aead->variable_nonce_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + if (out < in + in_len && in < out + aead->variable_nonce_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); + return 0; + } + OPENSSL_memcpy(out, nonce + aead->fixed_nonce_len, + aead->variable_nonce_len); + extra_len = aead->variable_nonce_len; + out += aead->variable_nonce_len; + max_out -= aead->variable_nonce_len; + } + + /* XOR the fixed nonce, if necessary. */ + if (aead->xor_fixed_nonce) { + assert(nonce_len == aead->fixed_nonce_len); + for (size_t i = 0; i < aead->fixed_nonce_len; i++) { + nonce[i] ^= aead->fixed_nonce[i]; + } + } + + if (!EVP_AEAD_CTX_seal(&aead->ctx, out, out_len, max_out, nonce, nonce_len, + in, in_len, ad, ad_len)) { + return 0; + } + *out_len += extra_len; + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_asn1.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_asn1.c new file mode 100644 index 000000000..cfcc12a85 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_asn1.c @@ -0,0 +1,824 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <limits.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/x509.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +/* An SSL_SESSION is serialized as the following ASN.1 structure: + * + * SSLSession ::= SEQUENCE { + * version INTEGER (1), -- session structure version + * sslVersion INTEGER, -- protocol version number + * cipher OCTET STRING, -- two bytes long + * sessionID OCTET STRING, + * masterKey OCTET STRING, + * time [1] INTEGER, -- seconds since UNIX epoch + * timeout [2] INTEGER, -- in seconds + * peer [3] Certificate OPTIONAL, + * sessionIDContext [4] OCTET STRING OPTIONAL, + * verifyResult [5] INTEGER OPTIONAL, -- one of X509_V_* codes + * hostName [6] OCTET STRING OPTIONAL, + * -- from server_name extension + * pskIdentity [8] OCTET STRING OPTIONAL, + * ticketLifetimeHint [9] INTEGER OPTIONAL, -- client-only + * ticket [10] OCTET STRING OPTIONAL, -- client-only + * peerSHA256 [13] OCTET STRING OPTIONAL, + * originalHandshakeHash [14] OCTET STRING OPTIONAL, + * signedCertTimestampList [15] OCTET STRING OPTIONAL, + * -- contents of SCT extension + * ocspResponse [16] OCTET STRING OPTIONAL, + * -- stapled OCSP response from the server + * extendedMasterSecret [17] BOOLEAN OPTIONAL, + * groupID [18] INTEGER OPTIONAL, + * -- For historical reasons, for legacy DHE or + * -- static RSA ciphers, this field contains + * -- another value to be discarded. + * certChain [19] SEQUENCE OF Certificate OPTIONAL, + * ticketAgeAdd [21] OCTET STRING OPTIONAL, + * isServer [22] BOOLEAN DEFAULT TRUE, + * peerSignatureAlgorithm [23] INTEGER OPTIONAL, + * ticketMaxEarlyData [24] INTEGER OPTIONAL, + * authTimeout [25] INTEGER OPTIONAL, -- defaults to timeout + * earlyALPN [26] OCTET STRING OPTIONAL, + * } + * + * Note: historically this serialization has included other optional + * fields. Their presence is currently treated as a parse error: + * + * keyArg [0] IMPLICIT OCTET STRING OPTIONAL, + * pskIdentityHint [7] OCTET STRING OPTIONAL, + * compressionMethod [11] OCTET STRING OPTIONAL, + * srpUsername [12] OCTET STRING OPTIONAL, + * ticketFlags [20] INTEGER OPTIONAL, + */ + +static const unsigned kVersion = 1; + +static const int kTimeTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1; +static const int kTimeoutTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2; +static const int kPeerTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3; +static const int kSessionIDContextTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 4; +static const int kVerifyResultTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 5; +static const int kHostNameTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 6; +static const int kPSKIdentityTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 8; +static const int kTicketLifetimeHintTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 9; +static const int kTicketTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 10; +static const int kPeerSHA256Tag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 13; +static const int kOriginalHandshakeHashTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 14; +static const int kSignedCertTimestampListTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 15; +static const int kOCSPResponseTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 16; +static const int kExtendedMasterSecretTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 17; +static const int kGroupIDTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 18; +static const int kCertChainTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 19; +static const int kTicketAgeAddTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 21; +static const int kIsServerTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 22; +static const int kPeerSignatureAlgorithmTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 23; +static const int kTicketMaxEarlyDataTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 24; +static const int kAuthTimeoutTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 25; +static const int kEarlyALPNTag = + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 26; + +static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data, + size_t *out_len, int for_ticket) { + CBB cbb, session, child, child2; + + if (in == NULL || in->cipher == NULL) { + return 0; + } + + CBB_zero(&cbb); + if (!CBB_init(&cbb, 0) || + !CBB_add_asn1(&cbb, &session, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1_uint64(&session, kVersion) || + !CBB_add_asn1_uint64(&session, in->ssl_version) || + !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) || + !CBB_add_u16(&child, (uint16_t)(in->cipher->id & 0xffff)) || + !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) || + /* The session ID is irrelevant for a session ticket. */ + !CBB_add_bytes(&child, in->session_id, + for_ticket ? 0 : in->session_id_length) || + !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child, in->master_key, in->master_key_length) || + !CBB_add_asn1(&session, &child, kTimeTag) || + !CBB_add_asn1_uint64(&child, in->time) || + !CBB_add_asn1(&session, &child, kTimeoutTag) || + !CBB_add_asn1_uint64(&child, in->timeout)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* The peer certificate is only serialized if the SHA-256 isn't + * serialized instead. */ + if (sk_CRYPTO_BUFFER_num(in->certs) > 0 && !in->peer_sha256_valid) { + const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs, 0); + if (!CBB_add_asn1(&session, &child, kPeerTag) || + !CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer), + CRYPTO_BUFFER_len(buffer))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + /* Although it is OPTIONAL and usually empty, OpenSSL has + * historically always encoded the sid_ctx. */ + if (!CBB_add_asn1(&session, &child, kSessionIDContextTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, in->sid_ctx, in->sid_ctx_length)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (in->verify_result != X509_V_OK) { + if (!CBB_add_asn1(&session, &child, kVerifyResultTag) || + !CBB_add_asn1_uint64(&child, in->verify_result)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->tlsext_hostname) { + if (!CBB_add_asn1(&session, &child, kHostNameTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, (const uint8_t *)in->tlsext_hostname, + strlen(in->tlsext_hostname))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->psk_identity) { + if (!CBB_add_asn1(&session, &child, kPSKIdentityTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, (const uint8_t *)in->psk_identity, + strlen(in->psk_identity))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->tlsext_tick_lifetime_hint > 0) { + if (!CBB_add_asn1(&session, &child, kTicketLifetimeHintTag) || + !CBB_add_asn1_uint64(&child, in->tlsext_tick_lifetime_hint)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->tlsext_tick && !for_ticket) { + if (!CBB_add_asn1(&session, &child, kTicketTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, in->tlsext_tick, in->tlsext_ticklen)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->peer_sha256_valid) { + if (!CBB_add_asn1(&session, &child, kPeerSHA256Tag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, in->peer_sha256, sizeof(in->peer_sha256))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->original_handshake_hash_len > 0) { + if (!CBB_add_asn1(&session, &child, kOriginalHandshakeHashTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, in->original_handshake_hash, + in->original_handshake_hash_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->tlsext_signed_cert_timestamp_list_length > 0) { + if (!CBB_add_asn1(&session, &child, kSignedCertTimestampListTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, in->tlsext_signed_cert_timestamp_list, + in->tlsext_signed_cert_timestamp_list_length)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->ocsp_response_length > 0) { + if (!CBB_add_asn1(&session, &child, kOCSPResponseTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, in->ocsp_response, in->ocsp_response_length)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->extended_master_secret) { + if (!CBB_add_asn1(&session, &child, kExtendedMasterSecretTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || + !CBB_add_u8(&child2, 0xff)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->group_id > 0 && + (!CBB_add_asn1(&session, &child, kGroupIDTag) || + !CBB_add_asn1_uint64(&child, in->group_id))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* The certificate chain is only serialized if the leaf's SHA-256 isn't + * serialized instead. */ + if (in->certs != NULL && + !in->peer_sha256_valid && + sk_CRYPTO_BUFFER_num(in->certs) >= 2) { + if (!CBB_add_asn1(&session, &child, kCertChainTag)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(in->certs); i++) { + const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(in->certs, i); + if (!CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer), + CRYPTO_BUFFER_len(buffer))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + } + + if (in->ticket_age_add_valid) { + if (!CBB_add_asn1(&session, &child, kTicketAgeAddTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_u32(&child2, in->ticket_age_add)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (!in->is_server) { + if (!CBB_add_asn1(&session, &child, kIsServerTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_BOOLEAN) || + !CBB_add_u8(&child2, 0x00)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->peer_signature_algorithm != 0 && + (!CBB_add_asn1(&session, &child, kPeerSignatureAlgorithmTag) || + !CBB_add_asn1_uint64(&child, in->peer_signature_algorithm))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (in->ticket_max_early_data != 0 && + (!CBB_add_asn1(&session, &child, kTicketMaxEarlyDataTag) || + !CBB_add_asn1_uint64(&child, in->ticket_max_early_data))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (in->timeout != in->auth_timeout && + (!CBB_add_asn1(&session, &child, kAuthTimeoutTag) || + !CBB_add_asn1_uint64(&child, in->auth_timeout))) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (in->early_alpn) { + if (!CBB_add_asn1(&session, &child, kEarlyALPNTag) || + !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) || + !CBB_add_bytes(&child2, (const uint8_t *)in->early_alpn, + in->early_alpn_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (!CBB_finish(&cbb, out_data, out_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + return 1; + + err: + CBB_cleanup(&cbb); + return 0; +} + +int SSL_SESSION_to_bytes(const SSL_SESSION *in, uint8_t **out_data, + size_t *out_len) { + if (in->not_resumable) { + /* If the caller has an unresumable session, e.g. if |SSL_get_session| were + * called on a TLS 1.3 or False Started connection, serialize with a + * placeholder value so it is not accidentally deserialized into a resumable + * one. */ + static const char kNotResumableSession[] = "NOT RESUMABLE"; + + *out_len = strlen(kNotResumableSession); + *out_data = BUF_memdup(kNotResumableSession, *out_len); + if (*out_data == NULL) { + return 0; + } + + return 1; + } + + return SSL_SESSION_to_bytes_full(in, out_data, out_len, 0); +} + +int SSL_SESSION_to_bytes_for_ticket(const SSL_SESSION *in, uint8_t **out_data, + size_t *out_len) { + return SSL_SESSION_to_bytes_full(in, out_data, out_len, 1); +} + +int i2d_SSL_SESSION(SSL_SESSION *in, uint8_t **pp) { + uint8_t *out; + size_t len; + + if (!SSL_SESSION_to_bytes(in, &out, &len)) { + return -1; + } + + if (len > INT_MAX) { + OPENSSL_free(out); + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return -1; + } + + if (pp) { + OPENSSL_memcpy(*pp, out, len); + *pp += len; + } + OPENSSL_free(out); + + return len; +} + +/* SSL_SESSION_parse_string gets an optional ASN.1 OCTET STRING + * explicitly tagged with |tag| from |cbs| and saves it in |*out|. On + * entry, if |*out| is not NULL, it frees the existing contents. If + * the element was not found, it sets |*out| to NULL. It returns one + * on success, whether or not the element was found, and zero on + * decode error. */ +static int SSL_SESSION_parse_string(CBS *cbs, char **out, unsigned tag) { + CBS value; + int present; + if (!CBS_get_optional_asn1_octet_string(cbs, &value, &present, tag)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; + } + if (present) { + if (CBS_contains_zero_byte(&value)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; + } + if (!CBS_strdup(&value, out)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + } else { + OPENSSL_free(*out); + *out = NULL; + } + return 1; +} + +/* SSL_SESSION_parse_string gets an optional ASN.1 OCTET STRING + * explicitly tagged with |tag| from |cbs| and stows it in |*out_ptr| + * and |*out_len|. If |*out_ptr| is not NULL, it frees the existing + * contents. On entry, if the element was not found, it sets + * |*out_ptr| to NULL. It returns one on success, whether or not the + * element was found, and zero on decode error. */ +static int SSL_SESSION_parse_octet_string(CBS *cbs, uint8_t **out_ptr, + size_t *out_len, unsigned tag) { + CBS value; + if (!CBS_get_optional_asn1_octet_string(cbs, &value, NULL, tag)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; + } + if (!CBS_stow(&value, out_ptr, out_len)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + return 1; +} + +/* SSL_SESSION_parse_bounded_octet_string parses an optional ASN.1 OCTET STRING + * explicitly tagged with |tag| of size at most |max_out|. */ +static int SSL_SESSION_parse_bounded_octet_string( + CBS *cbs, uint8_t *out, uint8_t *out_len, uint8_t max_out, unsigned tag) { + CBS value; + if (!CBS_get_optional_asn1_octet_string(cbs, &value, NULL, tag) || + CBS_len(&value) > max_out) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; + } + OPENSSL_memcpy(out, CBS_data(&value), CBS_len(&value)); + *out_len = (uint8_t)CBS_len(&value); + return 1; +} + +static int SSL_SESSION_parse_long(CBS *cbs, long *out, unsigned tag, + long default_value) { + uint64_t value; + if (!CBS_get_optional_asn1_uint64(cbs, &value, tag, + (uint64_t)default_value) || + value > LONG_MAX) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; + } + *out = (long)value; + return 1; +} + +static int SSL_SESSION_parse_u32(CBS *cbs, uint32_t *out, unsigned tag, + uint32_t default_value) { + uint64_t value; + if (!CBS_get_optional_asn1_uint64(cbs, &value, tag, + (uint64_t)default_value) || + value > 0xffffffff) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; + } + *out = (uint32_t)value; + return 1; +} + +static int SSL_SESSION_parse_u16(CBS *cbs, uint16_t *out, unsigned tag, + uint16_t default_value) { + uint64_t value; + if (!CBS_get_optional_asn1_uint64(cbs, &value, tag, + (uint64_t)default_value) || + value > 0xffff) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + return 0; + } + *out = (uint16_t)value; + return 1; +} + +SSL_SESSION *SSL_SESSION_parse(CBS *cbs, const SSL_X509_METHOD *x509_method, + CRYPTO_BUFFER_POOL *pool) { + SSL_SESSION *ret = ssl_session_new(x509_method); + if (ret == NULL) { + goto err; + } + + CBS session; + uint64_t version, ssl_version; + if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) || + !CBS_get_asn1_uint64(&session, &version) || + version != kVersion || + !CBS_get_asn1_uint64(&session, &ssl_version)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->ssl_version = ssl_version; + + CBS cipher; + uint16_t cipher_value; + if (!CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) || + !CBS_get_u16(&cipher, &cipher_value) || + CBS_len(&cipher) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->cipher = SSL_get_cipher_by_value(cipher_value); + if (ret->cipher == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_CIPHER); + goto err; + } + + CBS session_id, master_key; + if (!CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING) || + CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH || + !CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING) || + CBS_len(&master_key) > SSL_MAX_MASTER_KEY_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + OPENSSL_memcpy(ret->session_id, CBS_data(&session_id), CBS_len(&session_id)); + ret->session_id_length = CBS_len(&session_id); + OPENSSL_memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key)); + ret->master_key_length = CBS_len(&master_key); + + CBS child; + uint64_t timeout; + if (!CBS_get_asn1(&session, &child, kTimeTag) || + !CBS_get_asn1_uint64(&child, &ret->time) || + !CBS_get_asn1(&session, &child, kTimeoutTag) || + !CBS_get_asn1_uint64(&child, &timeout) || + timeout > UINT32_MAX) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + + ret->timeout = (uint32_t)timeout; + + CBS peer; + int has_peer; + if (!CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag) || + (has_peer && CBS_len(&peer) == 0)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + /* |peer| is processed with the certificate chain. */ + + if (!SSL_SESSION_parse_bounded_octet_string( + &session, ret->sid_ctx, &ret->sid_ctx_length, sizeof(ret->sid_ctx), + kSessionIDContextTag) || + !SSL_SESSION_parse_long(&session, &ret->verify_result, kVerifyResultTag, + X509_V_OK) || + !SSL_SESSION_parse_string(&session, &ret->tlsext_hostname, + kHostNameTag) || + !SSL_SESSION_parse_string(&session, &ret->psk_identity, + kPSKIdentityTag) || + !SSL_SESSION_parse_u32(&session, &ret->tlsext_tick_lifetime_hint, + kTicketLifetimeHintTag, 0) || + !SSL_SESSION_parse_octet_string(&session, &ret->tlsext_tick, + &ret->tlsext_ticklen, kTicketTag)) { + goto err; + } + + if (CBS_peek_asn1_tag(&session, kPeerSHA256Tag)) { + CBS peer_sha256; + if (!CBS_get_asn1(&session, &child, kPeerSHA256Tag) || + !CBS_get_asn1(&child, &peer_sha256, CBS_ASN1_OCTETSTRING) || + CBS_len(&peer_sha256) != sizeof(ret->peer_sha256) || + CBS_len(&child) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + OPENSSL_memcpy(ret->peer_sha256, CBS_data(&peer_sha256), + sizeof(ret->peer_sha256)); + ret->peer_sha256_valid = 1; + } else { + ret->peer_sha256_valid = 0; + } + + if (!SSL_SESSION_parse_bounded_octet_string( + &session, ret->original_handshake_hash, + &ret->original_handshake_hash_len, + sizeof(ret->original_handshake_hash), kOriginalHandshakeHashTag) || + !SSL_SESSION_parse_octet_string( + &session, &ret->tlsext_signed_cert_timestamp_list, + &ret->tlsext_signed_cert_timestamp_list_length, + kSignedCertTimestampListTag) || + !SSL_SESSION_parse_octet_string( + &session, &ret->ocsp_response, &ret->ocsp_response_length, + kOCSPResponseTag)) { + goto err; + } + + int extended_master_secret; + if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret, + kExtendedMasterSecretTag, + 0 /* default to false */)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->extended_master_secret = !!extended_master_secret; + + uint32_t value; + if (!SSL_SESSION_parse_u32(&session, &value, kGroupIDTag, 0)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + + /* Historically, the group_id field was used for key-exchange-specific + * information. Discard all but the group ID. */ + if (ret->cipher->algorithm_mkey & (SSL_kRSA | SSL_kDHE)) { + value = 0; + } + + if (value > 0xffff) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->group_id = (uint16_t)value; + + CBS cert_chain; + CBS_init(&cert_chain, NULL, 0); + int has_cert_chain; + if (!CBS_get_optional_asn1(&session, &cert_chain, &has_cert_chain, + kCertChainTag) || + (has_cert_chain && CBS_len(&cert_chain) == 0)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + if (has_cert_chain && !has_peer) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + if (has_peer || has_cert_chain) { + ret->certs = sk_CRYPTO_BUFFER_new_null(); + if (ret->certs == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (has_peer) { + /* TODO(agl): this should use the |SSL_CTX|'s pool. */ + CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&peer, pool); + if (buffer == NULL || + !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) { + CRYPTO_BUFFER_free(buffer); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + while (CBS_len(&cert_chain) > 0) { + CBS cert; + if (!CBS_get_any_asn1_element(&cert_chain, &cert, NULL, NULL) || + CBS_len(&cert) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + + /* TODO(agl): this should use the |SSL_CTX|'s pool. */ + CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new_from_CBS(&cert, pool); + if (buffer == NULL || + !sk_CRYPTO_BUFFER_push(ret->certs, buffer)) { + CRYPTO_BUFFER_free(buffer); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + } + + if (!x509_method->session_cache_objects(ret)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + + CBS age_add; + int age_add_present; + if (!CBS_get_optional_asn1_octet_string(&session, &age_add, &age_add_present, + kTicketAgeAddTag) || + (age_add_present && + !CBS_get_u32(&age_add, &ret->ticket_age_add)) || + CBS_len(&age_add) != 0) { + goto err; + } + ret->ticket_age_add_valid = age_add_present; + + int is_server; + if (!CBS_get_optional_asn1_bool(&session, &is_server, kIsServerTag, + 1 /* default to true */)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + /* TODO: in time we can include |is_server| for servers too, then we can + enforce that client and server sessions are never mixed up. */ + + ret->is_server = is_server; + + if (!SSL_SESSION_parse_u16(&session, &ret->peer_signature_algorithm, + kPeerSignatureAlgorithmTag, 0) || + !SSL_SESSION_parse_u32(&session, &ret->ticket_max_early_data, + kTicketMaxEarlyDataTag, 0) || + !SSL_SESSION_parse_u32(&session, &ret->auth_timeout, kAuthTimeoutTag, + ret->timeout) || + !SSL_SESSION_parse_octet_string(&session, &ret->early_alpn, + &ret->early_alpn_len, kEarlyALPNTag) || + CBS_len(&session) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + goto err; + } + + return ret; + +err: + SSL_SESSION_free(ret); + return NULL; +} + +SSL_SESSION *SSL_SESSION_from_bytes(const uint8_t *in, size_t in_len, + const SSL_CTX *ctx) { + CBS cbs; + CBS_init(&cbs, in, in_len); + SSL_SESSION *ret = SSL_SESSION_parse(&cbs, ctx->x509_method, ctx->pool); + if (ret == NULL) { + return NULL; + } + if (CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION); + SSL_SESSION_free(ret); + return NULL; + } + return ret; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_buffer.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_buffer.c new file mode 100644 index 000000000..c27db8ba8 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_buffer.c @@ -0,0 +1,312 @@ +/* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/type_check.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +OPENSSL_COMPILE_ASSERT(0xffff <= INT_MAX, uint16_fits_in_int); + +OPENSSL_COMPILE_ASSERT((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0, + align_to_a_power_of_two); + +/* setup_buffer initializes |buf| with capacity |cap|, aligned such that data + * written after |header_len| is aligned to a |SSL3_ALIGN_PAYLOAD|-byte + * boundary. It returns one on success and zero on error. */ +static int setup_buffer(SSL3_BUFFER *buf, size_t header_len, size_t cap) { + if (buf->buf != NULL || cap > 0xffff) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment. */ + buf->buf = OPENSSL_malloc(cap + SSL3_ALIGN_PAYLOAD - 1); + if (buf->buf == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + + /* Arrange the buffer such that the record body is aligned. */ + buf->offset = (0 - header_len - (uintptr_t)buf->buf) & + (SSL3_ALIGN_PAYLOAD - 1); + buf->len = 0; + buf->cap = cap; + return 1; +} + +static void consume_buffer(SSL3_BUFFER *buf, size_t len) { + if (len > buf->len) { + abort(); + } + buf->offset += (uint16_t)len; + buf->len -= (uint16_t)len; + buf->cap -= (uint16_t)len; +} + +static void clear_buffer(SSL3_BUFFER *buf) { + OPENSSL_free(buf->buf); + OPENSSL_memset(buf, 0, sizeof(SSL3_BUFFER)); +} + +OPENSSL_COMPILE_ASSERT(DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH <= + 0xffff, + maximum_read_buffer_too_large); + +/* setup_read_buffer initializes the read buffer if not already initialized. It + * returns one on success and zero on failure. */ +static int setup_read_buffer(SSL *ssl) { + SSL3_BUFFER *buf = &ssl->s3->read_buffer; + + if (buf->buf != NULL) { + return 1; + } + + size_t header_len = ssl_record_prefix_len(ssl); + size_t cap = SSL3_RT_MAX_ENCRYPTED_LENGTH; + if (SSL_is_dtls(ssl)) { + cap += DTLS1_RT_HEADER_LENGTH; + } else { + cap += SSL3_RT_HEADER_LENGTH; + } + + return setup_buffer(buf, header_len, cap); +} + +uint8_t *ssl_read_buffer(SSL *ssl) { + return ssl->s3->read_buffer.buf + ssl->s3->read_buffer.offset; +} + +size_t ssl_read_buffer_len(const SSL *ssl) { + return ssl->s3->read_buffer.len; +} + +static int dtls_read_buffer_next_packet(SSL *ssl) { + SSL3_BUFFER *buf = &ssl->s3->read_buffer; + + if (buf->len > 0) { + /* It is an error to call |dtls_read_buffer_extend| when the read buffer is + * not empty. */ + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* Read a single packet from |ssl->rbio|. |buf->cap| must fit in an int. */ + int ret = BIO_read(ssl->rbio, buf->buf + buf->offset, (int)buf->cap); + if (ret <= 0) { + ssl->rwstate = SSL_READING; + return ret; + } + /* |BIO_read| was bound by |buf->cap|, so this cannot overflow. */ + buf->len = (uint16_t)ret; + return 1; +} + +static int tls_read_buffer_extend_to(SSL *ssl, size_t len) { + SSL3_BUFFER *buf = &ssl->s3->read_buffer; + + if (len > buf->cap) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return -1; + } + + /* Read until the target length is reached. */ + while (buf->len < len) { + /* The amount of data to read is bounded by |buf->cap|, which must fit in an + * int. */ + int ret = BIO_read(ssl->rbio, buf->buf + buf->offset + buf->len, + (int)(len - buf->len)); + if (ret <= 0) { + ssl->rwstate = SSL_READING; + return ret; + } + /* |BIO_read| was bound by |buf->cap - buf->len|, so this cannot + * overflow. */ + buf->len += (uint16_t)ret; + } + + return 1; +} + +int ssl_read_buffer_extend_to(SSL *ssl, size_t len) { + /* |ssl_read_buffer_extend_to| implicitly discards any consumed data. */ + ssl_read_buffer_discard(ssl); + + if (!setup_read_buffer(ssl)) { + return -1; + } + + if (ssl->rbio == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET); + return -1; + } + + int ret; + if (SSL_is_dtls(ssl)) { + /* |len| is ignored for a datagram transport. */ + ret = dtls_read_buffer_next_packet(ssl); + } else { + ret = tls_read_buffer_extend_to(ssl, len); + } + + if (ret <= 0) { + /* If the buffer was empty originally and remained empty after attempting to + * extend it, release the buffer until the next attempt. */ + ssl_read_buffer_discard(ssl); + } + return ret; +} + +void ssl_read_buffer_consume(SSL *ssl, size_t len) { + SSL3_BUFFER *buf = &ssl->s3->read_buffer; + + consume_buffer(buf, len); + + /* The TLS stack never reads beyond the current record, so there will never be + * unconsumed data. If read-ahead is ever reimplemented, + * |ssl_read_buffer_discard| will require a |memcpy| to shift the excess back + * to the front of the buffer, to ensure there is enough space for the next + * record. */ + assert(SSL_is_dtls(ssl) || len == 0 || buf->len == 0); +} + +void ssl_read_buffer_discard(SSL *ssl) { + if (ssl->s3->read_buffer.len == 0) { + ssl_read_buffer_clear(ssl); + } +} + +void ssl_read_buffer_clear(SSL *ssl) { + clear_buffer(&ssl->s3->read_buffer); +} + + +int ssl_write_buffer_is_pending(const SSL *ssl) { + return ssl->s3->write_buffer.len > 0; +} + +OPENSSL_COMPILE_ASSERT(SSL3_RT_HEADER_LENGTH * 2 + + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD * 2 + + SSL3_RT_MAX_PLAIN_LENGTH <= 0xffff, + maximum_tls_write_buffer_too_large); + +OPENSSL_COMPILE_ASSERT(DTLS1_RT_HEADER_LENGTH + + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD + + SSL3_RT_MAX_PLAIN_LENGTH <= 0xffff, + maximum_dtls_write_buffer_too_large); + +int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len) { + SSL3_BUFFER *buf = &ssl->s3->write_buffer; + + if (buf->buf != NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + size_t header_len = ssl_seal_align_prefix_len(ssl); + + /* TODO(davidben): This matches the original behavior in keeping the malloc + * size consistent. Does this matter? |cap| could just be |max_len|. */ + size_t cap = SSL3_RT_MAX_PLAIN_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD; + if (SSL_is_dtls(ssl)) { + cap += DTLS1_RT_HEADER_LENGTH; + } else { + cap += SSL3_RT_HEADER_LENGTH; + if (ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) { + cap += SSL3_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD; + } + } + + if (max_len > cap) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + + if (!setup_buffer(buf, header_len, cap)) { + return 0; + } + *out_ptr = buf->buf + buf->offset; + return 1; +} + +void ssl_write_buffer_set_len(SSL *ssl, size_t len) { + SSL3_BUFFER *buf = &ssl->s3->write_buffer; + + if (len > buf->cap) { + abort(); + } + buf->len = len; +} + +static int tls_write_buffer_flush(SSL *ssl) { + SSL3_BUFFER *buf = &ssl->s3->write_buffer; + + while (buf->len > 0) { + int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len); + if (ret <= 0) { + ssl->rwstate = SSL_WRITING; + return ret; + } + consume_buffer(buf, (size_t)ret); + } + ssl_write_buffer_clear(ssl); + return 1; +} + +static int dtls_write_buffer_flush(SSL *ssl) { + SSL3_BUFFER *buf = &ssl->s3->write_buffer; + if (buf->len == 0) { + return 1; + } + + int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len); + if (ret <= 0) { + ssl->rwstate = SSL_WRITING; + /* If the write failed, drop the write buffer anyway. Datagram transports + * can't write half a packet, so the caller is expected to retry from the + * top. */ + ssl_write_buffer_clear(ssl); + return ret; + } + ssl_write_buffer_clear(ssl); + return 1; +} + +int ssl_write_buffer_flush(SSL *ssl) { + if (ssl->wbio == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET); + return -1; + } + + if (SSL_is_dtls(ssl)) { + return dtls_write_buffer_flush(ssl); + } else { + return tls_write_buffer_flush(ssl); + } +} + +void ssl_write_buffer_clear(SSL *ssl) { + clear_buffer(&ssl->s3->write_buffer); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_cert.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_cert.c new file mode 100644 index 000000000..5013b2057 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_cert.c @@ -0,0 +1,899 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include <openssl/bn.h> +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/dh.h> +#include <openssl/ec_key.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/sha.h> +#include <openssl/x509.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +CERT *ssl_cert_new(const SSL_X509_METHOD *x509_method) { + CERT *ret = OPENSSL_malloc(sizeof(CERT)); + if (ret == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return NULL; + } + OPENSSL_memset(ret, 0, sizeof(CERT)); + ret->x509_method = x509_method; + + return ret; +} + +static CRYPTO_BUFFER *buffer_up_ref(CRYPTO_BUFFER *buffer) { + CRYPTO_BUFFER_up_ref(buffer); + return buffer; +} + +CERT *ssl_cert_dup(CERT *cert) { + CERT *ret = OPENSSL_malloc(sizeof(CERT)); + if (ret == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return NULL; + } + OPENSSL_memset(ret, 0, sizeof(CERT)); + + ret->chain = sk_CRYPTO_BUFFER_deep_copy(cert->chain, buffer_up_ref, + CRYPTO_BUFFER_free); + + if (cert->privatekey != NULL) { + EVP_PKEY_up_ref(cert->privatekey); + ret->privatekey = cert->privatekey; + } + + ret->key_method = cert->key_method; + ret->x509_method = cert->x509_method; + + if (cert->dh_tmp != NULL) { + ret->dh_tmp = DHparams_dup(cert->dh_tmp); + if (ret->dh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); + goto err; + } + } + ret->dh_tmp_cb = cert->dh_tmp_cb; + + if (cert->sigalgs != NULL) { + ret->sigalgs = + BUF_memdup(cert->sigalgs, cert->num_sigalgs * sizeof(cert->sigalgs[0])); + if (ret->sigalgs == NULL) { + goto err; + } + } + ret->num_sigalgs = cert->num_sigalgs; + + ret->cert_cb = cert->cert_cb; + ret->cert_cb_arg = cert->cert_cb_arg; + + ret->x509_method->cert_dup(ret, cert); + + if (cert->signed_cert_timestamp_list != NULL) { + CRYPTO_BUFFER_up_ref(cert->signed_cert_timestamp_list); + ret->signed_cert_timestamp_list = cert->signed_cert_timestamp_list; + } + + if (cert->ocsp_response != NULL) { + CRYPTO_BUFFER_up_ref(cert->ocsp_response); + ret->ocsp_response = cert->ocsp_response; + } + + ret->sid_ctx_length = cert->sid_ctx_length; + OPENSSL_memcpy(ret->sid_ctx, cert->sid_ctx, sizeof(ret->sid_ctx)); + + return ret; + +err: + ssl_cert_free(ret); + return NULL; +} + +/* Free up and clear all certificates and chains */ +void ssl_cert_clear_certs(CERT *cert) { + if (cert == NULL) { + return; + } + + cert->x509_method->cert_clear(cert); + + sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free); + cert->chain = NULL; + EVP_PKEY_free(cert->privatekey); + cert->privatekey = NULL; + cert->key_method = NULL; +} + +void ssl_cert_free(CERT *c) { + if (c == NULL) { + return; + } + + DH_free(c->dh_tmp); + + ssl_cert_clear_certs(c); + c->x509_method->cert_free(c); + OPENSSL_free(c->sigalgs); + CRYPTO_BUFFER_free(c->signed_cert_timestamp_list); + CRYPTO_BUFFER_free(c->ocsp_response); + + OPENSSL_free(c); +} + +static void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), + void *arg) { + c->cert_cb = cb; + c->cert_cb_arg = arg; +} + +enum leaf_cert_and_privkey_result_t { + leaf_cert_and_privkey_error, + leaf_cert_and_privkey_ok, + leaf_cert_and_privkey_mismatch, +}; + +/* check_leaf_cert_and_privkey checks whether the certificate in |leaf_buffer| + * and the private key in |privkey| are suitable and coherent. It returns + * |leaf_cert_and_privkey_error| and pushes to the error queue if a problem is + * found. If the certificate and private key are valid, but incoherent, it + * returns |leaf_cert_and_privkey_mismatch|. Otherwise it returns + * |leaf_cert_and_privkey_ok|. */ +static enum leaf_cert_and_privkey_result_t check_leaf_cert_and_privkey( + CRYPTO_BUFFER *leaf_buffer, EVP_PKEY *privkey) { + enum leaf_cert_and_privkey_result_t ret = leaf_cert_and_privkey_error; + + CBS cert_cbs; + CRYPTO_BUFFER_init_CBS(leaf_buffer, &cert_cbs); + EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs); + if (pubkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto out; + } + + if (!ssl_is_key_type_supported(pubkey->type)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + goto out; + } + + /* An ECC certificate may be usable for ECDH or ECDSA. We only support ECDSA + * certificates, so sanity-check the key usage extension. */ + if (pubkey->type == EVP_PKEY_EC && + !ssl_cert_check_digital_signature_key_usage(&cert_cbs)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + goto out; + } + + if (privkey != NULL && + /* Sanity-check that the private key and the certificate match. */ + !ssl_compare_public_and_private_key(pubkey, privkey)) { + ERR_clear_error(); + ret = leaf_cert_and_privkey_mismatch; + goto out; + } + + ret = leaf_cert_and_privkey_ok; + +out: + EVP_PKEY_free(pubkey); + return ret; +} + +static int cert_set_chain_and_key( + CERT *cert, CRYPTO_BUFFER *const *certs, size_t num_certs, + EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method) { + if (num_certs == 0 || + (privkey == NULL && privkey_method == NULL)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (privkey != NULL && privkey_method != NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_HAVE_BOTH_PRIVKEY_AND_METHOD); + return 0; + } + + switch (check_leaf_cert_and_privkey(certs[0], privkey)) { + case leaf_cert_and_privkey_error: + return 0; + case leaf_cert_and_privkey_mismatch: + OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_AND_PRIVATE_KEY_MISMATCH); + return 0; + case leaf_cert_and_privkey_ok: + break; + } + + STACK_OF(CRYPTO_BUFFER) *certs_sk = sk_CRYPTO_BUFFER_new_null(); + if (certs_sk == NULL) { + return 0; + } + + for (size_t i = 0; i < num_certs; i++) { + if (!sk_CRYPTO_BUFFER_push(certs_sk, certs[i])) { + sk_CRYPTO_BUFFER_pop_free(certs_sk, CRYPTO_BUFFER_free); + return 0; + } + CRYPTO_BUFFER_up_ref(certs[i]); + } + + EVP_PKEY_free(cert->privatekey); + cert->privatekey = privkey; + if (privkey != NULL) { + EVP_PKEY_up_ref(privkey); + } + cert->key_method = privkey_method; + + sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free); + cert->chain = certs_sk; + + return 1; +} + +int SSL_set_chain_and_key(SSL *ssl, CRYPTO_BUFFER *const *certs, + size_t num_certs, EVP_PKEY *privkey, + const SSL_PRIVATE_KEY_METHOD *privkey_method) { + return cert_set_chain_and_key(ssl->cert, certs, num_certs, privkey, + privkey_method); +} + +int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs, + size_t num_certs, EVP_PKEY *privkey, + const SSL_PRIVATE_KEY_METHOD *privkey_method) { + return cert_set_chain_and_key(ctx->cert, certs, num_certs, privkey, + privkey_method); +} + +int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) { + switch (check_leaf_cert_and_privkey(buffer, cert->privatekey)) { + case leaf_cert_and_privkey_error: + return 0; + case leaf_cert_and_privkey_mismatch: + /* don't fail for a cert/key mismatch, just free current private key + * (when switching to a different cert & key, first this function should + * be used, then |ssl_set_pkey|. */ + EVP_PKEY_free(cert->privatekey); + cert->privatekey = NULL; + break; + case leaf_cert_and_privkey_ok: + break; + } + + cert->x509_method->cert_flush_cached_leaf(cert); + + if (cert->chain != NULL) { + CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(cert->chain, 0)); + sk_CRYPTO_BUFFER_set(cert->chain, 0, buffer); + CRYPTO_BUFFER_up_ref(buffer); + return 1; + } + + cert->chain = sk_CRYPTO_BUFFER_new_null(); + if (cert->chain == NULL) { + return 0; + } + + if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) { + sk_CRYPTO_BUFFER_free(cert->chain); + cert->chain = NULL; + return 0; + } + CRYPTO_BUFFER_up_ref(buffer); + + return 1; +} + +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, size_t der_len, + const uint8_t *der) { + CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL); + if (buffer == NULL) { + return 0; + } + + const int ok = ssl_set_cert(ctx->cert, buffer); + CRYPTO_BUFFER_free(buffer); + return ok; +} + +int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) { + CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL); + if (buffer == NULL) { + return 0; + } + + const int ok = ssl_set_cert(ssl->cert, buffer); + CRYPTO_BUFFER_free(buffer); + return ok; +} + +int ssl_has_certificate(const SSL *ssl) { + return ssl->cert->chain != NULL && + sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0) != NULL && + ssl_has_private_key(ssl); +} + +STACK_OF(CRYPTO_BUFFER) *ssl_parse_cert_chain(uint8_t *out_alert, + EVP_PKEY **out_pubkey, + uint8_t *out_leaf_sha256, + CBS *cbs, + CRYPTO_BUFFER_POOL *pool) { + *out_pubkey = NULL; + + STACK_OF(CRYPTO_BUFFER) *ret = sk_CRYPTO_BUFFER_new_null(); + if (ret == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return NULL; + } + + CBS certificate_list; + if (!CBS_get_u24_length_prefixed(cbs, &certificate_list)) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto err; + } + + while (CBS_len(&certificate_list) > 0) { + CBS certificate; + if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate) || + CBS_len(&certificate) == 0) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH); + goto err; + } + + if (sk_CRYPTO_BUFFER_num(ret) == 0) { + *out_pubkey = ssl_cert_parse_pubkey(&certificate); + if (*out_pubkey == NULL) { + *out_alert = SSL_AD_DECODE_ERROR; + goto err; + } + + /* Retain the hash of the leaf certificate if requested. */ + if (out_leaf_sha256 != NULL) { + SHA256(CBS_data(&certificate), CBS_len(&certificate), out_leaf_sha256); + } + } + + CRYPTO_BUFFER *buf = + CRYPTO_BUFFER_new_from_CBS(&certificate, pool); + if (buf == NULL) { + *out_alert = SSL_AD_DECODE_ERROR; + goto err; + } + + if (!sk_CRYPTO_BUFFER_push(ret, buf)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + CRYPTO_BUFFER_free(buf); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + return ret; + +err: + EVP_PKEY_free(*out_pubkey); + *out_pubkey = NULL; + sk_CRYPTO_BUFFER_pop_free(ret, CRYPTO_BUFFER_free); + return NULL; +} + +int ssl_add_cert_chain(SSL *ssl, CBB *cbb) { + if (!ssl_has_certificate(ssl)) { + return CBB_add_u24(cbb, 0); + } + + CBB certs; + if (!CBB_add_u24_length_prefixed(cbb, &certs)) { + goto err; + } + + STACK_OF(CRYPTO_BUFFER) *chain = ssl->cert->chain; + for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(chain); i++) { + CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(chain, i); + CBB child; + if (!CBB_add_u24_length_prefixed(&certs, &child) || + !CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer), + CRYPTO_BUFFER_len(buffer)) || + !CBB_flush(&certs)) { + goto err; + } + } + + return CBB_flush(cbb); + +err: + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; +} + +/* ssl_cert_skip_to_spki parses a DER-encoded, X.509 certificate from |in| and + * positions |*out_tbs_cert| to cover the TBSCertificate, starting at the + * subjectPublicKeyInfo. */ +static int ssl_cert_skip_to_spki(const CBS *in, CBS *out_tbs_cert) { + /* From RFC 5280, section 4.1 + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * serialNumber CertificateSerialNumber, + * signature AlgorithmIdentifier, + * issuer Name, + * validity Validity, + * subject Name, + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * ... } */ + CBS buf = *in; + + CBS toplevel; + if (!CBS_get_asn1(&buf, &toplevel, CBS_ASN1_SEQUENCE) || + CBS_len(&buf) != 0 || + !CBS_get_asn1(&toplevel, out_tbs_cert, CBS_ASN1_SEQUENCE) || + /* version */ + !CBS_get_optional_asn1( + out_tbs_cert, NULL, NULL, + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) || + /* serialNumber */ + !CBS_get_asn1(out_tbs_cert, NULL, CBS_ASN1_INTEGER) || + /* signature algorithm */ + !CBS_get_asn1(out_tbs_cert, NULL, CBS_ASN1_SEQUENCE) || + /* issuer */ + !CBS_get_asn1(out_tbs_cert, NULL, CBS_ASN1_SEQUENCE) || + /* validity */ + !CBS_get_asn1(out_tbs_cert, NULL, CBS_ASN1_SEQUENCE) || + /* subject */ + !CBS_get_asn1(out_tbs_cert, NULL, CBS_ASN1_SEQUENCE)) { + return 0; + } + + return 1; +} + +EVP_PKEY *ssl_cert_parse_pubkey(const CBS *in) { + CBS buf = *in, tbs_cert; + if (!ssl_cert_skip_to_spki(&buf, &tbs_cert)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT); + return NULL; + } + + return EVP_parse_public_key(&tbs_cert); +} + +int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey, + const EVP_PKEY *privkey) { + if (EVP_PKEY_is_opaque(privkey)) { + /* We cannot check an opaque private key and have to trust that it + * matches. */ + return 1; + } + + int ret = 0; + + switch (EVP_PKEY_cmp(pubkey, privkey)) { + case 1: + ret = 1; + break; + case 0: + OPENSSL_PUT_ERROR(X509, X509_R_KEY_VALUES_MISMATCH); + break; + case -1: + OPENSSL_PUT_ERROR(X509, X509_R_KEY_TYPE_MISMATCH); + break; + case -2: + OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_KEY_TYPE); + default: + assert(0); + break; + } + + return ret; +} + +int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey) { + if (privkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED); + return 0; + } + + if (cert->chain == NULL || + sk_CRYPTO_BUFFER_value(cert->chain, 0) == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED); + return 0; + } + + CBS cert_cbs; + CRYPTO_BUFFER_init_CBS(sk_CRYPTO_BUFFER_value(cert->chain, 0), &cert_cbs); + EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs); + if (!pubkey) { + OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_KEY_TYPE); + return 0; + } + + const int ok = ssl_compare_public_and_private_key(pubkey, privkey); + EVP_PKEY_free(pubkey); + return ok; +} + +int ssl_cert_check_digital_signature_key_usage(const CBS *in) { + CBS buf = *in; + + CBS tbs_cert, outer_extensions; + int has_extensions; + if (!ssl_cert_skip_to_spki(&buf, &tbs_cert) || + /* subjectPublicKeyInfo */ + !CBS_get_asn1(&tbs_cert, NULL, CBS_ASN1_SEQUENCE) || + /* issuerUniqueID */ + !CBS_get_optional_asn1( + &tbs_cert, NULL, NULL, + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1) || + /* subjectUniqueID */ + !CBS_get_optional_asn1( + &tbs_cert, NULL, NULL, + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2) || + !CBS_get_optional_asn1( + &tbs_cert, &outer_extensions, &has_extensions, + CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3)) { + goto parse_err; + } + + if (!has_extensions) { + return 1; + } + + CBS extensions; + if (!CBS_get_asn1(&outer_extensions, &extensions, CBS_ASN1_SEQUENCE)) { + goto parse_err; + } + + while (CBS_len(&extensions) > 0) { + CBS extension, oid, contents; + if (!CBS_get_asn1(&extensions, &extension, CBS_ASN1_SEQUENCE) || + !CBS_get_asn1(&extension, &oid, CBS_ASN1_OBJECT) || + (CBS_peek_asn1_tag(&extension, CBS_ASN1_BOOLEAN) && + !CBS_get_asn1(&extension, NULL, CBS_ASN1_BOOLEAN)) || + !CBS_get_asn1(&extension, &contents, CBS_ASN1_OCTETSTRING) || + CBS_len(&extension) != 0) { + goto parse_err; + } + + static const uint8_t kKeyUsageOID[3] = {0x55, 0x1d, 0x0f}; + if (CBS_len(&oid) != sizeof(kKeyUsageOID) || + OPENSSL_memcmp(CBS_data(&oid), kKeyUsageOID, sizeof(kKeyUsageOID)) != + 0) { + continue; + } + + CBS bit_string; + if (!CBS_get_asn1(&contents, &bit_string, CBS_ASN1_BITSTRING) || + CBS_len(&contents) != 0) { + goto parse_err; + } + + /* This is the KeyUsage extension. See + * https://tools.ietf.org/html/rfc5280#section-4.2.1.3 */ + if (!CBS_is_valid_asn1_bitstring(&bit_string)) { + goto parse_err; + } + + if (!CBS_asn1_bitstring_has_bit(&bit_string, 0)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ECC_CERT_NOT_FOR_SIGNING); + return 0; + } + + return 1; + } + + /* No KeyUsage extension found. */ + return 1; + +parse_err: + OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_PARSE_LEAF_CERT); + return 0; +} + +STACK_OF(CRYPTO_BUFFER) * + ssl_parse_client_CA_list(SSL *ssl, uint8_t *out_alert, CBS *cbs) { + CRYPTO_BUFFER_POOL *const pool = ssl->ctx->pool; + + STACK_OF(CRYPTO_BUFFER) *ret = sk_CRYPTO_BUFFER_new_null(); + if (ret == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return NULL; + } + + CBS child; + if (!CBS_get_u16_length_prefixed(cbs, &child)) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_LENGTH_MISMATCH); + goto err; + } + + while (CBS_len(&child) > 0) { + CBS distinguished_name; + if (!CBS_get_u16_length_prefixed(&child, &distinguished_name)) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_CA_DN_TOO_LONG); + goto err; + } + + CRYPTO_BUFFER *buffer = + CRYPTO_BUFFER_new_from_CBS(&distinguished_name, pool); + if (buffer == NULL || + !sk_CRYPTO_BUFFER_push(ret, buffer)) { + CRYPTO_BUFFER_free(buffer); + *out_alert = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (!ssl->ctx->x509_method->check_client_CA_list(ret)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto err; + } + + return ret; + +err: + sk_CRYPTO_BUFFER_pop_free(ret, CRYPTO_BUFFER_free); + return NULL; +} + +int ssl_add_client_CA_list(SSL *ssl, CBB *cbb) { + CBB child, name_cbb; + if (!CBB_add_u16_length_prefixed(cbb, &child)) { + return 0; + } + + STACK_OF(CRYPTO_BUFFER) *names = ssl->client_CA; + if (names == NULL) { + names = ssl->ctx->client_CA; + } + if (names == NULL) { + return CBB_flush(cbb); + } + + for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(names); i++) { + const CRYPTO_BUFFER *name = sk_CRYPTO_BUFFER_value(names, i); + + if (!CBB_add_u16_length_prefixed(&child, &name_cbb) || + !CBB_add_bytes(&name_cbb, CRYPTO_BUFFER_data(name), + CRYPTO_BUFFER_len(name))) { + return 0; + } + } + + return CBB_flush(cbb); +} + +void SSL_CTX_set_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, void *arg), + void *arg) { + ssl_cert_set_cert_cb(ctx->cert, cb, arg); +} + +void SSL_set_cert_cb(SSL *ssl, int (*cb)(SSL *ssl, void *arg), void *arg) { + ssl_cert_set_cert_cb(ssl->cert, cb, arg); +} + +STACK_OF(CRYPTO_BUFFER) *SSL_get0_peer_certificates(const SSL *ssl) { + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL) { + return NULL; + } + + return session->certs; +} + +STACK_OF(CRYPTO_BUFFER) *SSL_get0_server_requested_CAs(const SSL *ssl) { + if (ssl->s3->hs == NULL) { + return NULL; + } + return ssl->s3->hs->ca_names; +} + +int ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey, + const CRYPTO_BUFFER *leaf) { + SSL *const ssl = hs->ssl; + assert(ssl3_protocol_version(ssl) < TLS1_3_VERSION); + + /* Check the certificate's type matches the cipher. */ + int expected_type = ssl_cipher_get_key_type(hs->new_cipher); + assert(expected_type != EVP_PKEY_NONE); + if (pkey->type != expected_type) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CERTIFICATE_TYPE); + return 0; + } + + if (hs->new_cipher->algorithm_auth & SSL_aECDSA) { + CBS leaf_cbs; + CBS_init(&leaf_cbs, CRYPTO_BUFFER_data(leaf), CRYPTO_BUFFER_len(leaf)); + /* ECDSA and ECDH certificates use the same public key format. Instead, + * they are distinguished by the key usage extension in the certificate. */ + if (!ssl_cert_check_digital_signature_key_usage(&leaf_cbs)) { + return 0; + } + + EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); + if (ec_key == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECC_CERT); + return 0; + } + + /* Check the key's group and point format are acceptable. */ + uint16_t group_id; + if (!ssl_nid_to_group_id( + &group_id, EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key))) || + !tls1_check_group_id(ssl, group_id) || + EC_KEY_get_conv_form(ec_key) != POINT_CONVERSION_UNCOMPRESSED) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECC_CERT); + return 0; + } + } + + return 1; +} + +static int set_signed_cert_timestamp_list(CERT *cert, const uint8_t *list, + size_t list_len) { + CBS sct_list; + CBS_init(&sct_list, list, list_len); + if (!ssl_is_sct_list_valid(&sct_list)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SCT_LIST); + return 0; + } + + CRYPTO_BUFFER_free(cert->signed_cert_timestamp_list); + cert->signed_cert_timestamp_list = + CRYPTO_BUFFER_new(CBS_data(&sct_list), CBS_len(&sct_list), NULL); + return cert->signed_cert_timestamp_list != NULL; +} + +int SSL_CTX_set_signed_cert_timestamp_list(SSL_CTX *ctx, const uint8_t *list, + size_t list_len) { + return set_signed_cert_timestamp_list(ctx->cert, list, list_len); +} + +int SSL_set_signed_cert_timestamp_list(SSL *ssl, const uint8_t *list, + size_t list_len) { + return set_signed_cert_timestamp_list(ssl->cert, list, list_len); +} + +int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response, + size_t response_len) { + CRYPTO_BUFFER_free(ctx->cert->ocsp_response); + ctx->cert->ocsp_response = CRYPTO_BUFFER_new(response, response_len, NULL); + return ctx->cert->ocsp_response != NULL; +} + +int SSL_set_ocsp_response(SSL *ssl, const uint8_t *response, + size_t response_len) { + CRYPTO_BUFFER_free(ssl->cert->ocsp_response); + ssl->cert->ocsp_response = CRYPTO_BUFFER_new(response, response_len, NULL); + return ssl->cert->ocsp_response != NULL; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_cipher.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_cipher.c new file mode 100644 index 000000000..4ee3c1255 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_cipher.c @@ -0,0 +1,1867 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/sha.h> +#include <openssl/stack.h> + +#include "internal.h" +#include "../crypto/internal.h" + + +/* kCiphers is an array of all supported ciphers, sorted by id. */ +static const SSL_CIPHER kCiphers[] = { + /* The RSA ciphers */ + /* Cipher 02 */ + { + SSL3_TXT_RSA_NULL_SHA, + SSL3_CK_RSA_NULL_SHA, + SSL_kRSA, + SSL_aRSA, + SSL_eNULL, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher 0A */ + { + SSL3_TXT_RSA_DES_192_CBC3_SHA, + SSL3_CK_RSA_DES_192_CBC3_SHA, + SSL_kRSA, + SSL_aRSA, + SSL_3DES, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + + /* New AES ciphersuites */ + + /* Cipher 2F */ + { + TLS1_TXT_RSA_WITH_AES_128_SHA, + TLS1_CK_RSA_WITH_AES_128_SHA, + SSL_kRSA, + SSL_aRSA, + SSL_AES128, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher 33 */ + { + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA, + TLS1_CK_DHE_RSA_WITH_AES_128_SHA, + SSL_kDHE, + SSL_aRSA, + SSL_AES128, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher 35 */ + { + TLS1_TXT_RSA_WITH_AES_256_SHA, + TLS1_CK_RSA_WITH_AES_256_SHA, + SSL_kRSA, + SSL_aRSA, + SSL_AES256, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher 39 */ + { + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA, + TLS1_CK_DHE_RSA_WITH_AES_256_SHA, + SSL_kDHE, + SSL_aRSA, + SSL_AES256, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + + /* TLS v1.2 ciphersuites */ + + /* Cipher 3C */ + { + TLS1_TXT_RSA_WITH_AES_128_SHA256, + TLS1_CK_RSA_WITH_AES_128_SHA256, + SSL_kRSA, + SSL_aRSA, + SSL_AES128, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher 3D */ + { + TLS1_TXT_RSA_WITH_AES_256_SHA256, + TLS1_CK_RSA_WITH_AES_256_SHA256, + SSL_kRSA, + SSL_aRSA, + SSL_AES256, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher 67 */ + { + TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256, + TLS1_CK_DHE_RSA_WITH_AES_128_SHA256, + SSL_kDHE, + SSL_aRSA, + SSL_AES128, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher 6B */ + { + TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256, + TLS1_CK_DHE_RSA_WITH_AES_256_SHA256, + SSL_kDHE, + SSL_aRSA, + SSL_AES256, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* PSK cipher suites. */ + + /* Cipher 8C */ + { + TLS1_TXT_PSK_WITH_AES_128_CBC_SHA, + TLS1_CK_PSK_WITH_AES_128_CBC_SHA, + SSL_kPSK, + SSL_aPSK, + SSL_AES128, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher 8D */ + { + TLS1_TXT_PSK_WITH_AES_256_CBC_SHA, + TLS1_CK_PSK_WITH_AES_256_CBC_SHA, + SSL_kPSK, + SSL_aPSK, + SSL_AES256, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* GCM ciphersuites from RFC5288 */ + + /* Cipher 9C */ + { + TLS1_TXT_RSA_WITH_AES_128_GCM_SHA256, + TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, + SSL_kRSA, + SSL_aRSA, + SSL_AES128GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher 9D */ + { + TLS1_TXT_RSA_WITH_AES_256_GCM_SHA384, + TLS1_CK_RSA_WITH_AES_256_GCM_SHA384, + SSL_kRSA, + SSL_aRSA, + SSL_AES256GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA384, + }, + + /* Cipher 9E */ + { + TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256, + TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, + SSL_kDHE, + SSL_aRSA, + SSL_AES128GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher 9F */ + { + TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384, + TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384, + SSL_kDHE, + SSL_aRSA, + SSL_AES256GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA384, + }, + + /* TLS 1.3 suites. */ + + /* Cipher 1301 */ + { + TLS1_TXT_AES_128_GCM_SHA256, + TLS1_CK_AES_128_GCM_SHA256, + SSL_kGENERIC, + SSL_aGENERIC, + SSL_AES128GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher 1302 */ + { + TLS1_TXT_AES_256_GCM_SHA384, + TLS1_CK_AES_256_GCM_SHA384, + SSL_kGENERIC, + SSL_aGENERIC, + SSL_AES256GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA384, + }, + + /* Cipher 1303 */ + { + TLS1_TXT_CHACHA20_POLY1305_SHA256, + TLS1_CK_CHACHA20_POLY1305_SHA256, + SSL_kGENERIC, + SSL_aGENERIC, + SSL_CHACHA20POLY1305, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher C009 */ + { + TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + SSL_kECDHE, + SSL_aECDSA, + SSL_AES128, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher C00A */ + { + TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + SSL_kECDHE, + SSL_aECDSA, + SSL_AES256, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher C013 */ + { + TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, + SSL_kECDHE, + SSL_aRSA, + SSL_AES128, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher C014 */ + { + TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, + SSL_kECDHE, + SSL_aRSA, + SSL_AES256, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + + /* HMAC based TLS v1.2 ciphersuites from RFC5289 */ + + /* Cipher C023 */ + { + TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256, + TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256, + SSL_kECDHE, + SSL_aECDSA, + SSL_AES128, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher C024 */ + { + TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384, + TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384, + SSL_kECDHE, + SSL_aECDSA, + SSL_AES256, + SSL_SHA384, + SSL_HANDSHAKE_MAC_SHA384, + }, + + /* Cipher C027 */ + { + TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256, + TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256, + SSL_kECDHE, + SSL_aRSA, + SSL_AES128, + SSL_SHA256, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher C028 */ + { + TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384, + TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, + SSL_kECDHE, + SSL_aRSA, + SSL_AES256, + SSL_SHA384, + SSL_HANDSHAKE_MAC_SHA384, + }, + + + /* GCM based TLS v1.2 ciphersuites from RFC5289 */ + + /* Cipher C02B */ + { + TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + SSL_kECDHE, + SSL_aECDSA, + SSL_AES128GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher C02C */ + { + TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + SSL_kECDHE, + SSL_aECDSA, + SSL_AES256GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA384, + }, + + /* Cipher C02F */ + { + TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + SSL_kECDHE, + SSL_aRSA, + SSL_AES128GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher C030 */ + { + TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + SSL_kECDHE, + SSL_aRSA, + SSL_AES256GCM, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA384, + }, + + /* ECDHE-PSK cipher suites. */ + + /* Cipher C035 */ + { + TLS1_TXT_ECDHE_PSK_WITH_AES_128_CBC_SHA, + TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA, + SSL_kECDHE, + SSL_aPSK, + SSL_AES128, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* Cipher C036 */ + { + TLS1_TXT_ECDHE_PSK_WITH_AES_256_CBC_SHA, + TLS1_CK_ECDHE_PSK_WITH_AES_256_CBC_SHA, + SSL_kECDHE, + SSL_aPSK, + SSL_AES256, + SSL_SHA1, + SSL_HANDSHAKE_MAC_DEFAULT, + }, + + /* ChaCha20-Poly1305 cipher suites. */ + + /* Cipher CCA8 */ + { + TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + SSL_kECDHE, + SSL_aRSA, + SSL_CHACHA20POLY1305, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher CCA9 */ + { + TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + SSL_kECDHE, + SSL_aECDSA, + SSL_CHACHA20POLY1305, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + + /* Cipher CCAB */ + { + TLS1_TXT_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, + SSL_kECDHE, + SSL_aPSK, + SSL_CHACHA20POLY1305, + SSL_AEAD, + SSL_HANDSHAKE_MAC_SHA256, + }, + +}; + +static const size_t kCiphersLen = OPENSSL_ARRAY_SIZE(kCiphers); + +#define CIPHER_ADD 1 +#define CIPHER_KILL 2 +#define CIPHER_DEL 3 +#define CIPHER_ORD 4 +#define CIPHER_SPECIAL 5 + +typedef struct cipher_order_st { + const SSL_CIPHER *cipher; + int active; + int in_group; + struct cipher_order_st *next, *prev; +} CIPHER_ORDER; + +typedef struct cipher_alias_st { + /* name is the name of the cipher alias. */ + const char *name; + + /* The following fields are bitmasks for the corresponding fields on + * |SSL_CIPHER|. A cipher matches a cipher alias iff, for each bitmask, the + * bit corresponding to the cipher's value is set to 1. If any bitmask is + * all zeroes, the alias matches nothing. Use |~0u| for the default value. */ + uint32_t algorithm_mkey; + uint32_t algorithm_auth; + uint32_t algorithm_enc; + uint32_t algorithm_mac; + + /* min_version, if non-zero, matches all ciphers which were added in that + * particular protocol version. */ + uint16_t min_version; +} CIPHER_ALIAS; + +static const CIPHER_ALIAS kCipherAliases[] = { + /* "ALL" doesn't include eNULL. It must be explicitly enabled. */ + {"ALL", ~0u, ~0u, ~SSL_eNULL, ~0u, 0}, + + /* The "COMPLEMENTOFDEFAULT" rule is omitted. It matches nothing. */ + + /* key exchange aliases + * (some of those using only a single bit here combine + * multiple key exchange algs according to the RFCs, + * e.g. kEDH combines DHE_DSS and DHE_RSA) */ + {"kRSA", SSL_kRSA, ~0u, ~0u, ~0u, 0}, + + {"kDHE", SSL_kDHE, ~0u, ~0u, ~0u, 0}, + {"kEDH", SSL_kDHE, ~0u, ~0u, ~0u, 0}, + {"DH", SSL_kDHE, ~0u, ~0u, ~0u, 0}, + + {"kECDHE", SSL_kECDHE, ~0u, ~0u, ~0u, 0}, + {"kEECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0}, + {"ECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0}, + + {"kPSK", SSL_kPSK, ~0u, ~0u, ~0u, 0}, + + /* server authentication aliases */ + {"aRSA", ~0u, SSL_aRSA, ~SSL_eNULL, ~0u, 0}, + {"aECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, 0}, + {"ECDSA", ~0u, SSL_aECDSA, ~0u, ~0u, 0}, + {"aPSK", ~0u, SSL_aPSK, ~0u, ~0u, 0}, + + /* aliases combining key exchange and server authentication */ + {"DHE", SSL_kDHE, ~0u, ~0u, ~0u, 0}, + {"EDH", SSL_kDHE, ~0u, ~0u, ~0u, 0}, + {"ECDHE", SSL_kECDHE, ~0u, ~0u, ~0u, 0}, + {"EECDH", SSL_kECDHE, ~0u, ~0u, ~0u, 0}, + {"RSA", SSL_kRSA, SSL_aRSA, ~SSL_eNULL, ~0u, 0}, + {"PSK", SSL_kPSK, SSL_aPSK, ~0u, ~0u, 0}, + + /* symmetric encryption aliases */ + {"3DES", ~0u, ~0u, SSL_3DES, ~0u, 0}, + {"AES128", ~0u, ~0u, SSL_AES128 | SSL_AES128GCM, ~0u, 0}, + {"AES256", ~0u, ~0u, SSL_AES256 | SSL_AES256GCM, ~0u, 0}, + {"AES", ~0u, ~0u, SSL_AES, ~0u, 0}, + {"AESGCM", ~0u, ~0u, SSL_AES128GCM | SSL_AES256GCM, ~0u, 0}, + {"CHACHA20", ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, 0}, + + /* MAC aliases */ + {"SHA1", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0}, + {"SHA", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0}, + {"SHA256", ~0u, ~0u, ~0u, SSL_SHA256, 0}, + {"SHA384", ~0u, ~0u, ~0u, SSL_SHA384, 0}, + + /* Legacy protocol minimum version aliases. "TLSv1" is intentionally the + * same as "SSLv3". */ + {"SSLv3", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION}, + {"TLSv1", ~0u, ~0u, ~SSL_eNULL, ~0u, SSL3_VERSION}, + {"TLSv1.2", ~0u, ~0u, ~SSL_eNULL, ~0u, TLS1_2_VERSION}, + + /* Legacy strength classes. */ + {"HIGH", ~0u, ~0u, ~SSL_eNULL, ~0u, 0}, + {"FIPS", ~0u, ~0u, ~SSL_eNULL, ~0u, 0}, +}; + +static const size_t kCipherAliasesLen = OPENSSL_ARRAY_SIZE(kCipherAliases); + +static int ssl_cipher_id_cmp(const void *in_a, const void *in_b) { + const SSL_CIPHER *a = in_a; + const SSL_CIPHER *b = in_b; + + if (a->id > b->id) { + return 1; + } else if (a->id < b->id) { + return -1; + } else { + return 0; + } +} + +const SSL_CIPHER *SSL_get_cipher_by_value(uint16_t value) { + SSL_CIPHER c; + + c.id = 0x03000000L | value; + return bsearch(&c, kCiphers, kCiphersLen, sizeof(SSL_CIPHER), + ssl_cipher_id_cmp); +} + +int ssl_cipher_get_evp_aead(const EVP_AEAD **out_aead, + size_t *out_mac_secret_len, + size_t *out_fixed_iv_len, + const SSL_CIPHER *cipher, uint16_t version) { + *out_aead = NULL; + *out_mac_secret_len = 0; + *out_fixed_iv_len = 0; + + if (cipher->algorithm_mac == SSL_AEAD) { + if (cipher->algorithm_enc == SSL_AES128GCM) { + *out_aead = EVP_aead_aes_128_gcm(); + *out_fixed_iv_len = 4; + } else if (cipher->algorithm_enc == SSL_AES256GCM) { + *out_aead = EVP_aead_aes_256_gcm(); + *out_fixed_iv_len = 4; + } else if (cipher->algorithm_enc == SSL_CHACHA20POLY1305) { + *out_aead = EVP_aead_chacha20_poly1305(); + *out_fixed_iv_len = 12; + } else { + return 0; + } + + /* In TLS 1.3, the iv_len is equal to the AEAD nonce length whereas the code + * above computes the TLS 1.2 construction. */ + if (version >= TLS1_3_VERSION) { + *out_fixed_iv_len = EVP_AEAD_nonce_length(*out_aead); + } + } else if (cipher->algorithm_mac == SSL_SHA1) { + if (cipher->algorithm_enc == SSL_eNULL) { + if (version == SSL3_VERSION) { + *out_aead = EVP_aead_null_sha1_ssl3(); + } else { + *out_aead = EVP_aead_null_sha1_tls(); + } + } else if (cipher->algorithm_enc == SSL_3DES) { + if (version == SSL3_VERSION) { + *out_aead = EVP_aead_des_ede3_cbc_sha1_ssl3(); + *out_fixed_iv_len = 8; + } else if (version == TLS1_VERSION) { + *out_aead = EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv(); + *out_fixed_iv_len = 8; + } else { + *out_aead = EVP_aead_des_ede3_cbc_sha1_tls(); + } + } else if (cipher->algorithm_enc == SSL_AES128) { + if (version == SSL3_VERSION) { + *out_aead = EVP_aead_aes_128_cbc_sha1_ssl3(); + *out_fixed_iv_len = 16; + } else if (version == TLS1_VERSION) { + *out_aead = EVP_aead_aes_128_cbc_sha1_tls_implicit_iv(); + *out_fixed_iv_len = 16; + } else { + *out_aead = EVP_aead_aes_128_cbc_sha1_tls(); + } + } else if (cipher->algorithm_enc == SSL_AES256) { + if (version == SSL3_VERSION) { + *out_aead = EVP_aead_aes_256_cbc_sha1_ssl3(); + *out_fixed_iv_len = 16; + } else if (version == TLS1_VERSION) { + *out_aead = EVP_aead_aes_256_cbc_sha1_tls_implicit_iv(); + *out_fixed_iv_len = 16; + } else { + *out_aead = EVP_aead_aes_256_cbc_sha1_tls(); + } + } else { + return 0; + } + + *out_mac_secret_len = SHA_DIGEST_LENGTH; + } else if (cipher->algorithm_mac == SSL_SHA256) { + if (cipher->algorithm_enc == SSL_AES128) { + *out_aead = EVP_aead_aes_128_cbc_sha256_tls(); + } else if (cipher->algorithm_enc == SSL_AES256) { + *out_aead = EVP_aead_aes_256_cbc_sha256_tls(); + } else { + return 0; + } + + *out_mac_secret_len = SHA256_DIGEST_LENGTH; + } else if (cipher->algorithm_mac == SSL_SHA384) { + if (cipher->algorithm_enc != SSL_AES256) { + return 0; + } + + *out_aead = EVP_aead_aes_256_cbc_sha384_tls(); + *out_mac_secret_len = SHA384_DIGEST_LENGTH; + } else { + return 0; + } + + return 1; +} + +const EVP_MD *ssl_get_handshake_digest(uint32_t algorithm_prf, + uint16_t version) { + switch (algorithm_prf) { + case SSL_HANDSHAKE_MAC_DEFAULT: + return version >= TLS1_2_VERSION ? EVP_sha256() : EVP_md5_sha1(); + case SSL_HANDSHAKE_MAC_SHA256: + return EVP_sha256(); + case SSL_HANDSHAKE_MAC_SHA384: + return EVP_sha384(); + default: + return NULL; + } +} + +#define ITEM_SEP(a) \ + (((a) == ':') || ((a) == ' ') || ((a) == ';') || ((a) == ',')) + +/* rule_equals returns one iff the NUL-terminated string |rule| is equal to the + * |buf_len| bytes at |buf|. */ +static int rule_equals(const char *rule, const char *buf, size_t buf_len) { + /* |strncmp| alone only checks that |buf| is a prefix of |rule|. */ + return strncmp(rule, buf, buf_len) == 0 && rule[buf_len] == '\0'; +} + +static void ll_append_tail(CIPHER_ORDER **head, CIPHER_ORDER *curr, + CIPHER_ORDER **tail) { + if (curr == *tail) { + return; + } + if (curr == *head) { + *head = curr->next; + } + if (curr->prev != NULL) { + curr->prev->next = curr->next; + } + if (curr->next != NULL) { + curr->next->prev = curr->prev; + } + (*tail)->next = curr; + curr->prev = *tail; + curr->next = NULL; + *tail = curr; +} + +static void ll_append_head(CIPHER_ORDER **head, CIPHER_ORDER *curr, + CIPHER_ORDER **tail) { + if (curr == *head) { + return; + } + if (curr == *tail) { + *tail = curr->prev; + } + if (curr->next != NULL) { + curr->next->prev = curr->prev; + } + if (curr->prev != NULL) { + curr->prev->next = curr->next; + } + (*head)->prev = curr; + curr->next = *head; + curr->prev = NULL; + *head = curr; +} + +static void ssl_cipher_collect_ciphers(const SSL_PROTOCOL_METHOD *ssl_method, + CIPHER_ORDER *co_list, + CIPHER_ORDER **head_p, + CIPHER_ORDER **tail_p) { + /* The set of ciphers is static, but some subset may be unsupported by + * |ssl_method|, so the list may be smaller. */ + size_t co_list_num = 0; + for (size_t i = 0; i < kCiphersLen; i++) { + const SSL_CIPHER *cipher = &kCiphers[i]; + if (ssl_method->supports_cipher(cipher) && + /* TLS 1.3 ciphers do not participate in this mechanism. */ + cipher->algorithm_mkey != SSL_kGENERIC) { + co_list[co_list_num].cipher = cipher; + co_list[co_list_num].next = NULL; + co_list[co_list_num].prev = NULL; + co_list[co_list_num].active = 0; + co_list[co_list_num].in_group = 0; + co_list_num++; + } + } + + /* Prepare linked list from list entries. */ + if (co_list_num > 0) { + co_list[0].prev = NULL; + + if (co_list_num > 1) { + co_list[0].next = &co_list[1]; + + for (size_t i = 1; i < co_list_num - 1; i++) { + co_list[i].prev = &co_list[i - 1]; + co_list[i].next = &co_list[i + 1]; + } + + co_list[co_list_num - 1].prev = &co_list[co_list_num - 2]; + } + + co_list[co_list_num - 1].next = NULL; + + *head_p = &co_list[0]; + *tail_p = &co_list[co_list_num - 1]; + } +} + +/* ssl_cipher_apply_rule applies the rule type |rule| to ciphers matching its + * parameters in the linked list from |*head_p| to |*tail_p|. It writes the new + * head and tail of the list to |*head_p| and |*tail_p|, respectively. + * + * - If |cipher_id| is non-zero, only that cipher is selected. + * - Otherwise, if |strength_bits| is non-negative, it selects ciphers + * of that strength. + * - Otherwise, it selects ciphers that match each bitmasks in |alg_*| and + * |min_version|. */ +static void ssl_cipher_apply_rule( + uint32_t cipher_id, uint32_t alg_mkey, uint32_t alg_auth, + uint32_t alg_enc, uint32_t alg_mac, uint16_t min_version, int rule, + int strength_bits, int in_group, CIPHER_ORDER **head_p, + CIPHER_ORDER **tail_p) { + CIPHER_ORDER *head, *tail, *curr, *next, *last; + const SSL_CIPHER *cp; + int reverse = 0; + + if (cipher_id == 0 && strength_bits == -1 && min_version == 0 && + (alg_mkey == 0 || alg_auth == 0 || alg_enc == 0 || alg_mac == 0)) { + /* The rule matches nothing, so bail early. */ + return; + } + + if (rule == CIPHER_DEL) { + /* needed to maintain sorting between currently deleted ciphers */ + reverse = 1; + } + + head = *head_p; + tail = *tail_p; + + if (reverse) { + next = tail; + last = head; + } else { + next = head; + last = tail; + } + + curr = NULL; + for (;;) { + if (curr == last) { + break; + } + + curr = next; + if (curr == NULL) { + break; + } + + next = reverse ? curr->prev : curr->next; + cp = curr->cipher; + + /* Selection criteria is either a specific cipher, the value of + * |strength_bits|, or the algorithms used. */ + if (cipher_id != 0) { + if (cipher_id != cp->id) { + continue; + } + } else if (strength_bits >= 0) { + if (strength_bits != SSL_CIPHER_get_bits(cp, NULL)) { + continue; + } + } else { + if (!(alg_mkey & cp->algorithm_mkey) || + !(alg_auth & cp->algorithm_auth) || + !(alg_enc & cp->algorithm_enc) || + !(alg_mac & cp->algorithm_mac) || + (min_version != 0 && SSL_CIPHER_get_min_version(cp) != min_version)) { + continue; + } + } + + /* add the cipher if it has not been added yet. */ + if (rule == CIPHER_ADD) { + /* reverse == 0 */ + if (!curr->active) { + ll_append_tail(&head, curr, &tail); + curr->active = 1; + curr->in_group = in_group; + } + } + + /* Move the added cipher to this location */ + else if (rule == CIPHER_ORD) { + /* reverse == 0 */ + if (curr->active) { + ll_append_tail(&head, curr, &tail); + curr->in_group = 0; + } + } else if (rule == CIPHER_DEL) { + /* reverse == 1 */ + if (curr->active) { + /* most recently deleted ciphersuites get best positions + * for any future CIPHER_ADD (note that the CIPHER_DEL loop + * works in reverse to maintain the order) */ + ll_append_head(&head, curr, &tail); + curr->active = 0; + curr->in_group = 0; + } + } else if (rule == CIPHER_KILL) { + /* reverse == 0 */ + if (head == curr) { + head = curr->next; + } else { + curr->prev->next = curr->next; + } + + if (tail == curr) { + tail = curr->prev; + } + curr->active = 0; + if (curr->next != NULL) { + curr->next->prev = curr->prev; + } + if (curr->prev != NULL) { + curr->prev->next = curr->next; + } + curr->next = NULL; + curr->prev = NULL; + } + } + + *head_p = head; + *tail_p = tail; +} + +static int ssl_cipher_strength_sort(CIPHER_ORDER **head_p, + CIPHER_ORDER **tail_p) { + int max_strength_bits, i, *number_uses; + CIPHER_ORDER *curr; + + /* This routine sorts the ciphers with descending strength. The sorting must + * keep the pre-sorted sequence, so we apply the normal sorting routine as + * '+' movement to the end of the list. */ + max_strength_bits = 0; + curr = *head_p; + while (curr != NULL) { + if (curr->active && + SSL_CIPHER_get_bits(curr->cipher, NULL) > max_strength_bits) { + max_strength_bits = SSL_CIPHER_get_bits(curr->cipher, NULL); + } + curr = curr->next; + } + + number_uses = OPENSSL_malloc((max_strength_bits + 1) * sizeof(int)); + if (!number_uses) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + OPENSSL_memset(number_uses, 0, (max_strength_bits + 1) * sizeof(int)); + + /* Now find the strength_bits values actually used. */ + curr = *head_p; + while (curr != NULL) { + if (curr->active) { + number_uses[SSL_CIPHER_get_bits(curr->cipher, NULL)]++; + } + curr = curr->next; + } + + /* Go through the list of used strength_bits values in descending order. */ + for (i = max_strength_bits; i >= 0; i--) { + if (number_uses[i] > 0) { + ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, CIPHER_ORD, i, 0, head_p, tail_p); + } + } + + OPENSSL_free(number_uses); + return 1; +} + +static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method, + const char *rule_str, + CIPHER_ORDER **head_p, + CIPHER_ORDER **tail_p, int strict) { + uint32_t alg_mkey, alg_auth, alg_enc, alg_mac; + uint16_t min_version; + const char *l, *buf; + int multi, skip_rule, rule, ok, in_group = 0, has_group = 0; + size_t j, buf_len; + uint32_t cipher_id; + char ch; + + l = rule_str; + for (;;) { + ch = *l; + + if (ch == '\0') { + break; /* done */ + } + + if (in_group) { + if (ch == ']') { + if (*tail_p) { + (*tail_p)->in_group = 0; + } + in_group = 0; + l++; + continue; + } + + if (ch == '|') { + rule = CIPHER_ADD; + l++; + continue; + } else if (!(ch >= 'a' && ch <= 'z') && !(ch >= 'A' && ch <= 'Z') && + !(ch >= '0' && ch <= '9')) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_OPERATOR_IN_GROUP); + return 0; + } else { + rule = CIPHER_ADD; + } + } else if (ch == '-') { + rule = CIPHER_DEL; + l++; + } else if (ch == '+') { + rule = CIPHER_ORD; + l++; + } else if (ch == '!') { + rule = CIPHER_KILL; + l++; + } else if (ch == '@') { + rule = CIPHER_SPECIAL; + l++; + } else if (ch == '[') { + if (in_group) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NESTED_GROUP); + return 0; + } + in_group = 1; + has_group = 1; + l++; + continue; + } else { + rule = CIPHER_ADD; + } + + /* If preference groups are enabled, the only legal operator is +. + * Otherwise the in_group bits will get mixed up. */ + if (has_group && rule != CIPHER_ADD) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS); + return 0; + } + + if (ITEM_SEP(ch)) { + l++; + continue; + } + + multi = 0; + cipher_id = 0; + alg_mkey = ~0u; + alg_auth = ~0u; + alg_enc = ~0u; + alg_mac = ~0u; + min_version = 0; + skip_rule = 0; + + for (;;) { + ch = *l; + buf = l; + buf_len = 0; + while (((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) || + ((ch >= 'a') && (ch <= 'z')) || (ch == '-') || (ch == '.')) { + ch = *(++l); + buf_len++; + } + + if (buf_len == 0) { + /* We hit something we cannot deal with, it is no command or separator + * nor alphanumeric, so we call this an error. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND); + return 0; + } + + if (rule == CIPHER_SPECIAL) { + break; + } + + /* Look for a matching exact cipher. These aren't allowed in multipart + * rules. */ + if (!multi && ch != '+') { + for (j = 0; j < kCiphersLen; j++) { + const SSL_CIPHER *cipher = &kCiphers[j]; + if (rule_equals(cipher->name, buf, buf_len)) { + cipher_id = cipher->id; + break; + } + } + } + if (cipher_id == 0) { + /* If not an exact cipher, look for a matching cipher alias. */ + for (j = 0; j < kCipherAliasesLen; j++) { + if (rule_equals(kCipherAliases[j].name, buf, buf_len)) { + alg_mkey &= kCipherAliases[j].algorithm_mkey; + alg_auth &= kCipherAliases[j].algorithm_auth; + alg_enc &= kCipherAliases[j].algorithm_enc; + alg_mac &= kCipherAliases[j].algorithm_mac; + + if (min_version != 0 && + min_version != kCipherAliases[j].min_version) { + skip_rule = 1; + } else { + min_version = kCipherAliases[j].min_version; + } + break; + } + } + if (j == kCipherAliasesLen) { + skip_rule = 1; + if (strict) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND); + return 0; + } + } + } + + /* Check for a multipart rule. */ + if (ch != '+') { + break; + } + l++; + multi = 1; + } + + /* Ok, we have the rule, now apply it. */ + if (rule == CIPHER_SPECIAL) { + /* special command */ + ok = 0; + if (buf_len == 8 && !strncmp(buf, "STRENGTH", 8)) { + ok = ssl_cipher_strength_sort(head_p, tail_p); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND); + } + + if (ok == 0) { + return 0; + } + + /* We do not support any "multi" options together with "@", so throw away + * the rest of the command, if any left, until end or ':' is found. */ + while (*l != '\0' && !ITEM_SEP(*l)) { + l++; + } + } else if (!skip_rule) { + ssl_cipher_apply_rule(cipher_id, alg_mkey, alg_auth, alg_enc, alg_mac, + min_version, rule, -1, in_group, head_p, tail_p); + } + } + + if (in_group) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND); + return 0; + } + + return 1; +} + +int ssl_create_cipher_list( + const SSL_PROTOCOL_METHOD *ssl_method, + struct ssl_cipher_preference_list_st **out_cipher_list, + const char *rule_str, int strict) { + STACK_OF(SSL_CIPHER) *cipherstack = NULL; + CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr; + uint8_t *in_group_flags = NULL; + unsigned int num_in_group_flags = 0; + struct ssl_cipher_preference_list_st *pref_list = NULL; + + /* Return with error if nothing to do. */ + if (rule_str == NULL || out_cipher_list == NULL) { + return 0; + } + + /* Now we have to collect the available ciphers from the compiled in ciphers. + * We cannot get more than the number compiled in, so it is used for + * allocation. */ + co_list = OPENSSL_malloc(sizeof(CIPHER_ORDER) * kCiphersLen); + if (co_list == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + + ssl_cipher_collect_ciphers(ssl_method, co_list, &head, &tail); + + /* Now arrange all ciphers by preference: + * TODO(davidben): Compute this order once and copy it. */ + + /* Everything else being equal, prefer ECDHE_ECDSA and ECDHE_RSA over other + * key exchange mechanisms */ + ssl_cipher_apply_rule(0, SSL_kECDHE, SSL_aECDSA, ~0u, ~0u, 0, CIPHER_ADD, -1, + 0, &head, &tail); + ssl_cipher_apply_rule(0, SSL_kECDHE, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, 0, CIPHER_DEL, -1, 0, &head, + &tail); + + /* Order the bulk ciphers. First the preferred AEAD ciphers. We prefer + * CHACHA20 unless there is hardware support for fast and constant-time + * AES_GCM. Of the two CHACHA20 variants, the new one is preferred over the + * old one. */ + if (EVP_has_aes_hardware()) { + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, 0, CIPHER_ADD, + -1, 0, &head, &tail); + } else { + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, 0, CIPHER_ADD, + -1, 0, &head, &tail); + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0, + &head, &tail); + } + + /* Then the legacy non-AEAD ciphers: AES_128_CBC, AES_256_CBC, + * 3DES_EDE_CBC_SHA. */ + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128, ~0u, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256, ~0u, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_3DES, ~0u, 0, CIPHER_ADD, -1, 0, &head, + &tail); + + /* Temporarily enable everything else for sorting */ + ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, 0, CIPHER_ADD, -1, 0, &head, + &tail); + + /* Move ciphers without forward secrecy to the end. */ + ssl_cipher_apply_rule(0, (SSL_kRSA | SSL_kPSK), ~0u, ~0u, ~0u, 0, + CIPHER_ORD, -1, 0, &head, &tail); + + /* Now disable everything (maintaining the ordering!) */ + ssl_cipher_apply_rule(0, ~0u, ~0u, ~0u, ~0u, 0, CIPHER_DEL, -1, 0, &head, + &tail); + + /* If the rule_string begins with DEFAULT, apply the default rule before + * using the (possibly available) additional rules. */ + const char *rule_p = rule_str; + if (strncmp(rule_str, "DEFAULT", 7) == 0) { + if (!ssl_cipher_process_rulestr(ssl_method, SSL_DEFAULT_CIPHER_LIST, &head, + &tail, strict)) { + goto err; + } + rule_p += 7; + if (*rule_p == ':') { + rule_p++; + } + } + + if (*rule_p != '\0' && + !ssl_cipher_process_rulestr(ssl_method, rule_p, &head, &tail, strict)) { + goto err; + } + + /* Allocate new "cipherstack" for the result, return with error + * if we cannot get one. */ + cipherstack = sk_SSL_CIPHER_new_null(); + if (cipherstack == NULL) { + goto err; + } + + in_group_flags = OPENSSL_malloc(kCiphersLen); + if (!in_group_flags) { + goto err; + } + + /* The cipher selection for the list is done. The ciphers are added + * to the resulting precedence to the STACK_OF(SSL_CIPHER). */ + for (curr = head; curr != NULL; curr = curr->next) { + if (curr->active) { + if (!sk_SSL_CIPHER_push(cipherstack, curr->cipher)) { + goto err; + } + in_group_flags[num_in_group_flags++] = curr->in_group; + } + } + OPENSSL_free(co_list); /* Not needed any longer */ + co_list = NULL; + + pref_list = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st)); + if (!pref_list) { + goto err; + } + pref_list->ciphers = cipherstack; + pref_list->in_group_flags = OPENSSL_malloc(num_in_group_flags); + if (!pref_list->in_group_flags) { + goto err; + } + OPENSSL_memcpy(pref_list->in_group_flags, in_group_flags, num_in_group_flags); + OPENSSL_free(in_group_flags); + in_group_flags = NULL; + if (*out_cipher_list != NULL) { + ssl_cipher_preference_list_free(*out_cipher_list); + } + *out_cipher_list = pref_list; + pref_list = NULL; + + /* Configuring an empty cipher list is an error but still updates the + * output. */ + if (sk_SSL_CIPHER_num((*out_cipher_list)->ciphers) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH); + return 0; + } + + return 1; + +err: + OPENSSL_free(co_list); + OPENSSL_free(in_group_flags); + sk_SSL_CIPHER_free(cipherstack); + if (pref_list) { + OPENSSL_free(pref_list->in_group_flags); + } + OPENSSL_free(pref_list); + return 0; +} + +uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *cipher) { return cipher->id; } + +uint16_t ssl_cipher_get_value(const SSL_CIPHER *cipher) { + uint32_t id = cipher->id; + /* All ciphers are SSLv3. */ + assert((id & 0xff000000) == 0x03000000); + return id & 0xffff; +} + +int SSL_CIPHER_is_AES(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_AES) != 0; +} + +int SSL_CIPHER_has_SHA1_HMAC(const SSL_CIPHER *cipher) { + return (cipher->algorithm_mac & SSL_SHA1) != 0; +} + +int SSL_CIPHER_has_SHA256_HMAC(const SSL_CIPHER *cipher) { + return (cipher->algorithm_mac & SSL_SHA256) != 0; +} + +int SSL_CIPHER_is_AEAD(const SSL_CIPHER *cipher) { + return (cipher->algorithm_mac & SSL_AEAD) != 0; +} + +int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) != 0; +} + +int SSL_CIPHER_is_AES128GCM(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_AES128GCM) != 0; +} + +int SSL_CIPHER_is_AES128CBC(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_AES128) != 0; +} + +int SSL_CIPHER_is_AES256CBC(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_AES256) != 0; +} + +int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_CHACHA20POLY1305) != 0; +} + +int SSL_CIPHER_is_NULL(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_eNULL) != 0; +} + +int SSL_CIPHER_is_block_cipher(const SSL_CIPHER *cipher) { + return (cipher->algorithm_enc & SSL_eNULL) == 0 && + cipher->algorithm_mac != SSL_AEAD; +} + +int SSL_CIPHER_is_ECDSA(const SSL_CIPHER *cipher) { + return (cipher->algorithm_auth & SSL_aECDSA) != 0; +} + +int SSL_CIPHER_is_DHE(const SSL_CIPHER *cipher) { + return (cipher->algorithm_mkey & SSL_kDHE) != 0; +} + +int SSL_CIPHER_is_ECDHE(const SSL_CIPHER *cipher) { + return (cipher->algorithm_mkey & SSL_kECDHE) != 0; +} + +int SSL_CIPHER_is_static_RSA(const SSL_CIPHER *cipher) { + return (cipher->algorithm_mkey & SSL_kRSA) != 0; +} + +uint16_t SSL_CIPHER_get_min_version(const SSL_CIPHER *cipher) { + if (cipher->algorithm_mkey == SSL_kGENERIC || + cipher->algorithm_auth == SSL_aGENERIC) { + return TLS1_3_VERSION; + } + + if (cipher->algorithm_prf != SSL_HANDSHAKE_MAC_DEFAULT) { + /* Cipher suites before TLS 1.2 use the default PRF, while all those added + * afterwards specify a particular hash. */ + return TLS1_2_VERSION; + } + return SSL3_VERSION; +} + +uint16_t SSL_CIPHER_get_max_version(const SSL_CIPHER *cipher) { + if (cipher->algorithm_mkey == SSL_kGENERIC || + cipher->algorithm_auth == SSL_aGENERIC) { + return TLS1_3_VERSION; + } + return TLS1_2_VERSION; +} + +/* return the actual cipher being used */ +const char *SSL_CIPHER_get_name(const SSL_CIPHER *cipher) { + if (cipher != NULL) { + return cipher->name; + } + + return "(NONE)"; +} + +const char *SSL_CIPHER_get_kx_name(const SSL_CIPHER *cipher) { + if (cipher == NULL) { + return ""; + } + + switch (cipher->algorithm_mkey) { + case SSL_kRSA: + return "RSA"; + + case SSL_kDHE: + switch (cipher->algorithm_auth) { + case SSL_aRSA: + return "DHE_RSA"; + default: + assert(0); + return "UNKNOWN"; + } + + case SSL_kECDHE: + switch (cipher->algorithm_auth) { + case SSL_aECDSA: + return "ECDHE_ECDSA"; + case SSL_aRSA: + return "ECDHE_RSA"; + case SSL_aPSK: + return "ECDHE_PSK"; + default: + assert(0); + return "UNKNOWN"; + } + + case SSL_kPSK: + assert(cipher->algorithm_auth == SSL_aPSK); + return "PSK"; + + case SSL_kGENERIC: + assert(cipher->algorithm_auth == SSL_aGENERIC); + return "GENERIC"; + + default: + assert(0); + return "UNKNOWN"; + } +} + +static const char *ssl_cipher_get_enc_name(const SSL_CIPHER *cipher) { + switch (cipher->algorithm_enc) { + case SSL_3DES: + return "3DES_EDE_CBC"; + case SSL_AES128: + return "AES_128_CBC"; + case SSL_AES256: + return "AES_256_CBC"; + case SSL_AES128GCM: + return "AES_128_GCM"; + case SSL_AES256GCM: + return "AES_256_GCM"; + case SSL_CHACHA20POLY1305: + return "CHACHA20_POLY1305"; + break; + default: + assert(0); + return "UNKNOWN"; + } +} + +static const char *ssl_cipher_get_prf_name(const SSL_CIPHER *cipher) { + switch (cipher->algorithm_prf) { + case SSL_HANDSHAKE_MAC_DEFAULT: + /* Before TLS 1.2, the PRF component is the hash used in the HMAC, which + * is SHA-1 for all supported ciphers. */ + assert(cipher->algorithm_mac == SSL_SHA1); + return "SHA"; + case SSL_HANDSHAKE_MAC_SHA256: + return "SHA256"; + case SSL_HANDSHAKE_MAC_SHA384: + return "SHA384"; + } + assert(0); + return "UNKNOWN"; +} + +char *SSL_CIPHER_get_rfc_name(const SSL_CIPHER *cipher) { + if (cipher == NULL) { + return NULL; + } + + const char *kx_name = SSL_CIPHER_get_kx_name(cipher); + const char *enc_name = ssl_cipher_get_enc_name(cipher); + const char *prf_name = ssl_cipher_get_prf_name(cipher); + + /* The final name is TLS_{kx_name}_WITH_{enc_name}_{prf_name} or + * TLS_{enc_name}_{prf_name} depending on whether the cipher is AEAD-only. */ + size_t len = 4 + strlen(enc_name) + 1 + strlen(prf_name) + 1; + + if (cipher->algorithm_mkey != SSL_kGENERIC) { + len += strlen(kx_name) + 6; + } + + char *ret = OPENSSL_malloc(len); + if (ret == NULL) { + return NULL; + } + + if (BUF_strlcpy(ret, "TLS_", len) >= len || + (cipher->algorithm_mkey != SSL_kGENERIC && + (BUF_strlcat(ret, kx_name, len) >= len || + BUF_strlcat(ret, "_WITH_", len) >= len)) || + BUF_strlcat(ret, enc_name, len) >= len || + BUF_strlcat(ret, "_", len) >= len || + BUF_strlcat(ret, prf_name, len) >= len) { + assert(0); + OPENSSL_free(ret); + return NULL; + } + + assert(strlen(ret) + 1 == len); + return ret; +} + +int SSL_CIPHER_get_bits(const SSL_CIPHER *cipher, int *out_alg_bits) { + if (cipher == NULL) { + return 0; + } + + int alg_bits, strength_bits; + switch (cipher->algorithm_enc) { + case SSL_AES128: + case SSL_AES128GCM: + alg_bits = 128; + strength_bits = 128; + break; + + case SSL_AES256: + case SSL_AES256GCM: + case SSL_CHACHA20POLY1305: + alg_bits = 256; + strength_bits = 256; + break; + + case SSL_3DES: + alg_bits = 168; + strength_bits = 112; + break; + + case SSL_eNULL: + alg_bits = 0; + strength_bits = 0; + break; + + default: + assert(0); + alg_bits = 0; + strength_bits = 0; + } + + if (out_alg_bits != NULL) { + *out_alg_bits = alg_bits; + } + return strength_bits; +} + +const char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, + int len) { + const char *kx, *au, *enc, *mac; + uint32_t alg_mkey, alg_auth, alg_enc, alg_mac; + + alg_mkey = cipher->algorithm_mkey; + alg_auth = cipher->algorithm_auth; + alg_enc = cipher->algorithm_enc; + alg_mac = cipher->algorithm_mac; + + switch (alg_mkey) { + case SSL_kRSA: + kx = "RSA"; + break; + + case SSL_kDHE: + kx = "DH"; + break; + + case SSL_kECDHE: + kx = "ECDH"; + break; + + case SSL_kPSK: + kx = "PSK"; + break; + + case SSL_kGENERIC: + kx = "GENERIC"; + break; + + default: + kx = "unknown"; + } + + switch (alg_auth) { + case SSL_aRSA: + au = "RSA"; + break; + + case SSL_aECDSA: + au = "ECDSA"; + break; + + case SSL_aPSK: + au = "PSK"; + break; + + case SSL_aGENERIC: + au = "GENERIC"; + break; + + default: + au = "unknown"; + break; + } + + switch (alg_enc) { + case SSL_3DES: + enc = "3DES(168)"; + break; + + case SSL_AES128: + enc = "AES(128)"; + break; + + case SSL_AES256: + enc = "AES(256)"; + break; + + case SSL_AES128GCM: + enc = "AESGCM(128)"; + break; + + case SSL_AES256GCM: + enc = "AESGCM(256)"; + break; + + case SSL_CHACHA20POLY1305: + enc = "ChaCha20-Poly1305"; + break; + + case SSL_eNULL: + enc="None"; + break; + + default: + enc = "unknown"; + break; + } + + switch (alg_mac) { + case SSL_SHA1: + mac = "SHA1"; + break; + + case SSL_SHA256: + mac = "SHA256"; + break; + + case SSL_SHA384: + mac = "SHA384"; + break; + + case SSL_AEAD: + mac = "AEAD"; + break; + + default: + mac = "unknown"; + break; + } + + if (buf == NULL) { + len = 128; + buf = OPENSSL_malloc(len); + if (buf == NULL) { + return NULL; + } + } else if (len < 128) { + return "Buffer too small"; + } + + BIO_snprintf(buf, len, "%-23s Kx=%-8s Au=%-4s Enc=%-9s Mac=%-4s\n", + cipher->name, kx, au, enc, mac); + return buf; +} + +const char *SSL_CIPHER_get_version(const SSL_CIPHER *cipher) { + return "TLSv1/SSLv3"; +} + +COMP_METHOD *SSL_COMP_get_compression_methods(void) { return NULL; } + +int SSL_COMP_add_compression_method(int id, COMP_METHOD *cm) { return 1; } + +const char *SSL_COMP_get_name(const COMP_METHOD *comp) { return NULL; } + +void SSL_COMP_free_compression_methods(void) {} + +int ssl_cipher_get_key_type(const SSL_CIPHER *cipher) { + uint32_t alg_a = cipher->algorithm_auth; + + if (alg_a & SSL_aECDSA) { + return EVP_PKEY_EC; + } else if (alg_a & SSL_aRSA) { + return EVP_PKEY_RSA; + } + + return EVP_PKEY_NONE; +} + +int ssl_cipher_uses_certificate_auth(const SSL_CIPHER *cipher) { + return (cipher->algorithm_auth & SSL_aCERT) != 0; +} + +int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher) { + /* Ephemeral Diffie-Hellman key exchanges require a ServerKeyExchange. */ + if (cipher->algorithm_mkey & SSL_kDHE || + cipher->algorithm_mkey & SSL_kECDHE) { + return 1; + } + + /* It is optional in all others. */ + return 0; +} + +size_t ssl_cipher_get_record_split_len(const SSL_CIPHER *cipher) { + size_t block_size; + switch (cipher->algorithm_enc) { + case SSL_3DES: + block_size = 8; + break; + case SSL_AES128: + case SSL_AES256: + block_size = 16; + break; + default: + return 0; + } + + /* All supported TLS 1.0 ciphers use SHA-1. */ + assert(cipher->algorithm_mac == SSL_SHA1); + size_t ret = 1 + SHA_DIGEST_LENGTH; + ret += block_size - (ret % block_size); + return ret; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_ecdh.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_ecdh.c new file mode 100644 index 000000000..f49d5661a --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_ecdh.c @@ -0,0 +1,465 @@ +/* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bn.h> +#include <openssl/bytestring.h> +#include <openssl/curve25519.h> +#include <openssl/ec.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/nid.h> + +#include "internal.h" +#include "../crypto/internal.h" + + +/* |EC_POINT| implementation. */ + +static void ssl_ec_point_cleanup(SSL_ECDH_CTX *ctx) { + BIGNUM *private_key = (BIGNUM *)ctx->data; + BN_clear_free(private_key); +} + +static int ssl_ec_point_offer(SSL_ECDH_CTX *ctx, CBB *out) { + assert(ctx->data == NULL); + BIGNUM *private_key = BN_new(); + if (private_key == NULL) { + return 0; + } + ctx->data = private_key; + + /* Set up a shared |BN_CTX| for all operations. */ + BN_CTX *bn_ctx = BN_CTX_new(); + if (bn_ctx == NULL) { + return 0; + } + BN_CTX_start(bn_ctx); + + int ret = 0; + EC_POINT *public_key = NULL; + EC_GROUP *group = EC_GROUP_new_by_curve_name(ctx->method->nid); + if (group == NULL) { + goto err; + } + + /* Generate a private key. */ + if (!BN_rand_range_ex(private_key, 1, EC_GROUP_get0_order(group))) { + goto err; + } + + /* Compute the corresponding public key and serialize it. */ + public_key = EC_POINT_new(group); + if (public_key == NULL || + !EC_POINT_mul(group, public_key, private_key, NULL, NULL, bn_ctx) || + !EC_POINT_point2cbb(out, group, public_key, POINT_CONVERSION_UNCOMPRESSED, + bn_ctx)) { + goto err; + } + + ret = 1; + +err: + EC_GROUP_free(group); + EC_POINT_free(public_key); + BN_CTX_end(bn_ctx); + BN_CTX_free(bn_ctx); + return ret; +} + +static int ssl_ec_point_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret, + size_t *out_secret_len, uint8_t *out_alert, + const uint8_t *peer_key, size_t peer_key_len) { + BIGNUM *private_key = (BIGNUM *)ctx->data; + assert(private_key != NULL); + *out_alert = SSL_AD_INTERNAL_ERROR; + + /* Set up a shared |BN_CTX| for all operations. */ + BN_CTX *bn_ctx = BN_CTX_new(); + if (bn_ctx == NULL) { + return 0; + } + BN_CTX_start(bn_ctx); + + int ret = 0; + EC_GROUP *group = EC_GROUP_new_by_curve_name(ctx->method->nid); + EC_POINT *peer_point = NULL, *result = NULL; + uint8_t *secret = NULL; + if (group == NULL) { + goto err; + } + + /* Compute the x-coordinate of |peer_key| * |private_key|. */ + peer_point = EC_POINT_new(group); + result = EC_POINT_new(group); + if (peer_point == NULL || result == NULL) { + goto err; + } + BIGNUM *x = BN_CTX_get(bn_ctx); + if (x == NULL) { + goto err; + } + if (!EC_POINT_oct2point(group, peer_point, peer_key, peer_key_len, bn_ctx)) { + *out_alert = SSL_AD_DECODE_ERROR; + goto err; + } + if (!EC_POINT_mul(group, result, NULL, peer_point, private_key, bn_ctx) || + !EC_POINT_get_affine_coordinates_GFp(group, result, x, NULL, bn_ctx)) { + goto err; + } + + /* Encode the x-coordinate left-padded with zeros. */ + size_t secret_len = (EC_GROUP_get_degree(group) + 7) / 8; + secret = OPENSSL_malloc(secret_len); + if (secret == NULL || !BN_bn2bin_padded(secret, secret_len, x)) { + goto err; + } + + *out_secret = secret; + *out_secret_len = secret_len; + secret = NULL; + ret = 1; + +err: + EC_GROUP_free(group); + EC_POINT_free(peer_point); + EC_POINT_free(result); + BN_CTX_end(bn_ctx); + BN_CTX_free(bn_ctx); + OPENSSL_free(secret); + return ret; +} + +static int ssl_ec_point_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key, + uint8_t **out_secret, size_t *out_secret_len, + uint8_t *out_alert, const uint8_t *peer_key, + size_t peer_key_len) { + *out_alert = SSL_AD_INTERNAL_ERROR; + if (!ssl_ec_point_offer(ctx, out_public_key) || + !ssl_ec_point_finish(ctx, out_secret, out_secret_len, out_alert, peer_key, + peer_key_len)) { + return 0; + } + return 1; +} + +/* X25119 implementation. */ + +static void ssl_x25519_cleanup(SSL_ECDH_CTX *ctx) { + if (ctx->data == NULL) { + return; + } + OPENSSL_cleanse(ctx->data, 32); + OPENSSL_free(ctx->data); +} + +static int ssl_x25519_offer(SSL_ECDH_CTX *ctx, CBB *out) { + assert(ctx->data == NULL); + + ctx->data = OPENSSL_malloc(32); + if (ctx->data == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + uint8_t public_key[32]; + X25519_keypair(public_key, (uint8_t *)ctx->data); + return CBB_add_bytes(out, public_key, sizeof(public_key)); +} + +static int ssl_x25519_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret, + size_t *out_secret_len, uint8_t *out_alert, + const uint8_t *peer_key, size_t peer_key_len) { + assert(ctx->data != NULL); + *out_alert = SSL_AD_INTERNAL_ERROR; + + uint8_t *secret = OPENSSL_malloc(32); + if (secret == NULL) { + return 0; + } + + if (peer_key_len != 32 || + !X25519(secret, (uint8_t *)ctx->data, peer_key)) { + OPENSSL_free(secret); + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); + return 0; + } + + *out_secret = secret; + *out_secret_len = 32; + return 1; +} + +static int ssl_x25519_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key, + uint8_t **out_secret, size_t *out_secret_len, + uint8_t *out_alert, const uint8_t *peer_key, + size_t peer_key_len) { + *out_alert = SSL_AD_INTERNAL_ERROR; + if (!ssl_x25519_offer(ctx, out_public_key) || + !ssl_x25519_finish(ctx, out_secret, out_secret_len, out_alert, peer_key, + peer_key_len)) { + return 0; + } + return 1; +} + + +/* Legacy DHE-based implementation. */ + +static void ssl_dhe_cleanup(SSL_ECDH_CTX *ctx) { + DH_free((DH *)ctx->data); +} + +static int ssl_dhe_offer(SSL_ECDH_CTX *ctx, CBB *out) { + DH *dh = (DH *)ctx->data; + /* The group must have been initialized already, but not the key. */ + assert(dh != NULL); + assert(dh->priv_key == NULL); + + /* Due to a bug in yaSSL, the public key must be zero padded to the size of + * the prime. */ + return DH_generate_key(dh) && + BN_bn2cbb_padded(out, BN_num_bytes(dh->p), dh->pub_key); +} + +static int ssl_dhe_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret, + size_t *out_secret_len, uint8_t *out_alert, + const uint8_t *peer_key, size_t peer_key_len) { + DH *dh = (DH *)ctx->data; + assert(dh != NULL); + assert(dh->priv_key != NULL); + *out_alert = SSL_AD_INTERNAL_ERROR; + + int secret_len = 0; + uint8_t *secret = NULL; + BIGNUM *peer_point = BN_bin2bn(peer_key, peer_key_len, NULL); + if (peer_point == NULL) { + goto err; + } + + secret = OPENSSL_malloc(DH_size(dh)); + if (secret == NULL) { + goto err; + } + secret_len = DH_compute_key(secret, peer_point, dh); + if (secret_len <= 0) { + goto err; + } + + *out_secret = secret; + *out_secret_len = (size_t)secret_len; + BN_free(peer_point); + return 1; + +err: + if (secret_len > 0) { + OPENSSL_cleanse(secret, (size_t)secret_len); + } + OPENSSL_free(secret); + BN_free(peer_point); + return 0; +} + +static int ssl_dhe_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key, + uint8_t **out_secret, size_t *out_secret_len, + uint8_t *out_alert, const uint8_t *peer_key, + size_t peer_key_len) { + *out_alert = SSL_AD_INTERNAL_ERROR; + if (!ssl_dhe_offer(ctx, out_public_key) || + !ssl_dhe_finish(ctx, out_secret, out_secret_len, out_alert, peer_key, + peer_key_len)) { + return 0; + } + return 1; +} + +static const SSL_ECDH_METHOD kDHEMethod = { + NID_undef, 0, "", + ssl_dhe_cleanup, + ssl_dhe_offer, + ssl_dhe_accept, + ssl_dhe_finish, + CBS_get_u16_length_prefixed, + CBB_add_u16_length_prefixed, +}; + +static const SSL_ECDH_METHOD kMethods[] = { + { + NID_X9_62_prime256v1, + SSL_CURVE_SECP256R1, + "P-256", + ssl_ec_point_cleanup, + ssl_ec_point_offer, + ssl_ec_point_accept, + ssl_ec_point_finish, + CBS_get_u8_length_prefixed, + CBB_add_u8_length_prefixed, + }, + { + NID_secp384r1, + SSL_CURVE_SECP384R1, + "P-384", + ssl_ec_point_cleanup, + ssl_ec_point_offer, + ssl_ec_point_accept, + ssl_ec_point_finish, + CBS_get_u8_length_prefixed, + CBB_add_u8_length_prefixed, + }, + { + NID_secp521r1, + SSL_CURVE_SECP521R1, + "P-521", + ssl_ec_point_cleanup, + ssl_ec_point_offer, + ssl_ec_point_accept, + ssl_ec_point_finish, + CBS_get_u8_length_prefixed, + CBB_add_u8_length_prefixed, + }, + { + NID_X25519, + SSL_CURVE_X25519, + "X25519", + ssl_x25519_cleanup, + ssl_x25519_offer, + ssl_x25519_accept, + ssl_x25519_finish, + CBS_get_u8_length_prefixed, + CBB_add_u8_length_prefixed, + }, +}; + +static const SSL_ECDH_METHOD *method_from_group_id(uint16_t group_id) { + for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kMethods); i++) { + if (kMethods[i].group_id == group_id) { + return &kMethods[i]; + } + } + return NULL; +} + +static const SSL_ECDH_METHOD *method_from_nid(int nid) { + for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kMethods); i++) { + if (kMethods[i].nid == nid) { + return &kMethods[i]; + } + } + return NULL; +} + +static const SSL_ECDH_METHOD *method_from_name(const char *name, size_t len) { + for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kMethods); i++) { + if (len == strlen(kMethods[i].name) && + !strncmp(kMethods[i].name, name, len)) { + return &kMethods[i]; + } + } + return NULL; +} + +const char* SSL_get_curve_name(uint16_t group_id) { + const SSL_ECDH_METHOD *method = method_from_group_id(group_id); + if (method == NULL) { + return NULL; + } + return method->name; +} + +int ssl_nid_to_group_id(uint16_t *out_group_id, int nid) { + const SSL_ECDH_METHOD *method = method_from_nid(nid); + if (method == NULL) { + return 0; + } + *out_group_id = method->group_id; + return 1; +} + +int ssl_name_to_group_id(uint16_t *out_group_id, const char *name, size_t len) { + const SSL_ECDH_METHOD *method = method_from_name(name, len); + if (method == NULL) { + return 0; + } + *out_group_id = method->group_id; + return 1; +} + +int SSL_ECDH_CTX_init(SSL_ECDH_CTX *ctx, uint16_t group_id) { + SSL_ECDH_CTX_cleanup(ctx); + + const SSL_ECDH_METHOD *method = method_from_group_id(group_id); + if (method == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE); + return 0; + } + ctx->method = method; + return 1; +} + +void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params) { + SSL_ECDH_CTX_cleanup(ctx); + + ctx->method = &kDHEMethod; + ctx->data = params; +} + +void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx) { + if (ctx->method == NULL) { + return; + } + ctx->method->cleanup(ctx); + ctx->method = NULL; + ctx->data = NULL; +} + +uint16_t SSL_ECDH_CTX_get_id(const SSL_ECDH_CTX *ctx) { + return ctx->method->group_id; +} + +int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out) { + if (ctx->method == NULL) { + return 0; + } + return ctx->method->get_key(cbs, out); +} + +int SSL_ECDH_CTX_add_key(SSL_ECDH_CTX *ctx, CBB *cbb, CBB *out_contents) { + if (ctx->method == NULL) { + return 0; + } + return ctx->method->add_key(cbb, out_contents); +} + +int SSL_ECDH_CTX_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key) { + return ctx->method->offer(ctx, out_public_key); +} + +int SSL_ECDH_CTX_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key, + uint8_t **out_secret, size_t *out_secret_len, + uint8_t *out_alert, const uint8_t *peer_key, + size_t peer_key_len) { + return ctx->method->accept(ctx, out_public_key, out_secret, out_secret_len, + out_alert, peer_key, peer_key_len); +} + +int SSL_ECDH_CTX_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret, + size_t *out_secret_len, uint8_t *out_alert, + const uint8_t *peer_key, size_t peer_key_len) { + return ctx->method->finish(ctx, out_secret, out_secret_len, out_alert, + peer_key, peer_key_len); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_file.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_file.c new file mode 100644 index 000000000..59351a32f --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_file.c @@ -0,0 +1,575 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +#include <openssl/ssl.h> + +#include <errno.h> +#include <string.h> + +#include <openssl/asn1.h> +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/pem.h> +#include <openssl/stack.h> +#include <openssl/x509.h> + +#include "internal.h" + + +static int xname_cmp(const X509_NAME **a, const X509_NAME **b) { + return X509_NAME_cmp(*a, *b); +} + +/* TODO(davidben): Is there any reason this doesn't call + * |SSL_add_file_cert_subjects_to_stack|? */ +STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file) { + BIO *in; + X509 *x = NULL; + X509_NAME *xn = NULL; + STACK_OF(X509_NAME) *ret = NULL, *sk; + + sk = sk_X509_NAME_new(xname_cmp); + in = BIO_new(BIO_s_file()); + + if (sk == NULL || in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!BIO_read_filename(in, file)) { + goto err; + } + + for (;;) { + if (PEM_read_bio_X509(in, &x, NULL, NULL) == NULL) { + break; + } + if (ret == NULL) { + ret = sk_X509_NAME_new_null(); + if (ret == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + xn = X509_get_subject_name(x); + if (xn == NULL) { + goto err; + } + + /* Check for duplicates. */ + if (sk_X509_NAME_find(sk, NULL, xn)) { + continue; + } + + xn = X509_NAME_dup(xn); + if (xn == NULL || + !sk_X509_NAME_push(sk /* non-owning */, xn) || + !sk_X509_NAME_push(ret /* owning */, xn)) { + X509_NAME_free(xn); + goto err; + } + } + + if (0) { + err: + sk_X509_NAME_pop_free(ret, X509_NAME_free); + ret = NULL; + } + + sk_X509_NAME_free(sk); + BIO_free(in); + X509_free(x); + if (ret != NULL) { + ERR_clear_error(); + } + return ret; +} + +int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack, + const char *file) { + BIO *in; + X509 *x = NULL; + X509_NAME *xn = NULL; + int ret = 0; + int (*oldcmp)(const X509_NAME **a, const X509_NAME **b); + + oldcmp = sk_X509_NAME_set_cmp_func(stack, xname_cmp); + in = BIO_new(BIO_s_file()); + + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!BIO_read_filename(in, file)) { + goto err; + } + + for (;;) { + if (PEM_read_bio_X509(in, &x, NULL, NULL) == NULL) { + break; + } + xn = X509_get_subject_name(x); + if (xn == NULL) { + goto err; + } + + /* Check for duplicates. */ + if (sk_X509_NAME_find(stack, NULL, xn)) { + continue; + } + + xn = X509_NAME_dup(xn); + if (xn == NULL || + !sk_X509_NAME_push(stack, xn)) { + X509_NAME_free(xn); + goto err; + } + } + + ERR_clear_error(); + ret = 1; + +err: + BIO_free(in); + X509_free(x); + + (void) sk_X509_NAME_set_cmp_func(stack, oldcmp); + + return ret; +} + +int SSL_use_certificate_file(SSL *ssl, const char *file, int type) { + int reason_code; + BIO *in; + int ret = 0; + X509 *x = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + x = d2i_X509_bio(in, NULL); + } else if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + x = PEM_read_bio_X509(in, NULL, ssl->ctx->default_passwd_callback, + ssl->ctx->default_passwd_callback_userdata); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + + ret = SSL_use_certificate(ssl, x); + +end: + X509_free(x); + BIO_free(in); + + return ret; +} + +int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type) { + int reason_code, ret = 0; + BIO *in; + RSA *rsa = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + rsa = d2i_RSAPrivateKey_bio(in, NULL); + } else if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + rsa = + PEM_read_bio_RSAPrivateKey(in, NULL, ssl->ctx->default_passwd_callback, + ssl->ctx->default_passwd_callback_userdata); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_use_RSAPrivateKey(ssl, rsa); + RSA_free(rsa); + +end: + BIO_free(in); + return ret; +} + +int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type) { + int reason_code, ret = 0; + BIO *in; + EVP_PKEY *pkey = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + pkey = PEM_read_bio_PrivateKey(in, NULL, ssl->ctx->default_passwd_callback, + ssl->ctx->default_passwd_callback_userdata); + } else if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + pkey = d2i_PrivateKey_bio(in, NULL); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_use_PrivateKey(ssl, pkey); + EVP_PKEY_free(pkey); + +end: + BIO_free(in); + return ret; +} + +int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) { + int reason_code; + BIO *in; + int ret = 0; + X509 *x = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + x = d2i_X509_bio(in, NULL); + } else if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + x = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + +end: + X509_free(x); + BIO_free(in); + return ret; +} + +int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type) { + int reason_code, ret = 0; + BIO *in; + RSA *rsa = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + rsa = d2i_RSAPrivateKey_bio(in, NULL); + } else if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + rsa = PEM_read_bio_RSAPrivateKey(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_CTX_use_RSAPrivateKey(ctx, rsa); + RSA_free(rsa); + +end: + BIO_free(in); + return ret; +} + +int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) { + int reason_code, ret = 0; + BIO *in; + EVP_PKEY *pkey = NULL; + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + if (type == SSL_FILETYPE_PEM) { + reason_code = ERR_R_PEM_LIB; + pkey = PEM_read_bio_PrivateKey(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + } else if (type == SSL_FILETYPE_ASN1) { + reason_code = ERR_R_ASN1_LIB; + pkey = d2i_PrivateKey_bio(in, NULL); + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, reason_code); + goto end; + } + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); + +end: + BIO_free(in); + return ret; +} + +/* Read a file that contains our certificate in "PEM" format, possibly followed + * by a sequence of CA certificates that should be sent to the peer in the + * Certificate message. */ +int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) { + BIO *in; + int ret = 0; + X509 *x = NULL; + + ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */ + + in = BIO_new(BIO_s_file()); + if (in == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SYS_LIB); + goto end; + } + + x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB); + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + + if (ERR_peek_error() != 0) { + ret = 0; /* Key/certificate mismatch doesn't imply ret==0 ... */ + } + + if (ret) { + /* If we could set up our certificate, now proceed to the CA + * certificates. */ + X509 *ca; + int r; + uint32_t err; + + SSL_CTX_clear_chain_certs(ctx); + + while ((ca = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata)) != + NULL) { + r = SSL_CTX_add0_chain_cert(ctx, ca); + if (!r) { + X509_free(ca); + ret = 0; + goto end; + } + /* Note that we must not free r if it was successfully added to the chain + * (while we must free the main certificate, since its reference count is + * increased by SSL_CTX_use_certificate). */ + } + + /* When the while loop ends, it's usually just EOF. */ + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { + ERR_clear_error(); + } else { + ret = 0; /* some real error */ + } + } + +end: + X509_free(x); + BIO_free(in); + return ret; +} + +void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) { + ctx->default_passwd_callback = cb; +} + +void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *data) { + ctx->default_passwd_callback_userdata = data; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_lib.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_lib.c new file mode 100644 index 000000000..d16c952f1 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_lib.c @@ -0,0 +1,2709 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/crypto.h> +#include <openssl/dh.h> +#include <openssl/err.h> +#include <openssl/lhash.h> +#include <openssl/mem.h> +#include <openssl/rand.h> + +#include "internal.h" +#include "../crypto/internal.h" + +#if defined(OPENSSL_WINDOWS) +#include <sys/timeb.h> +#else +#include <sys/socket.h> +#include <sys/time.h> +#endif + + +/* |SSL_R_UNKNOWN_PROTOCOL| is no longer emitted, but continue to define it + * to avoid downstream churn. */ +OPENSSL_DECLARE_ERROR_REASON(SSL, UNKNOWN_PROTOCOL) + +/* The following errors are no longer emitted, but are used in nginx without + * #ifdefs. */ +OPENSSL_DECLARE_ERROR_REASON(SSL, BLOCK_CIPHER_PAD_IS_WRONG) +OPENSSL_DECLARE_ERROR_REASON(SSL, NO_CIPHERS_SPECIFIED) + +/* Some error codes are special. Ensure the make_errors.go script never + * regresses this. */ +OPENSSL_COMPILE_ASSERT(SSL_R_TLSV1_ALERT_NO_RENEGOTIATION == + SSL_AD_NO_RENEGOTIATION + SSL_AD_REASON_OFFSET, + ssl_alert_reason_code_mismatch); + +/* kMaxHandshakeSize is the maximum size, in bytes, of a handshake message. */ +static const size_t kMaxHandshakeSize = (1u << 24) - 1; + +static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl = + CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA; +static CRYPTO_EX_DATA_CLASS g_ex_data_class_ssl_ctx = + CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA; + +int SSL_library_init(void) { + CRYPTO_library_init(); + return 1; +} + +static uint32_t ssl_session_hash(const SSL_SESSION *sess) { + const uint8_t *session_id = sess->session_id; + + uint8_t tmp_storage[sizeof(uint32_t)]; + if (sess->session_id_length < sizeof(tmp_storage)) { + OPENSSL_memset(tmp_storage, 0, sizeof(tmp_storage)); + OPENSSL_memcpy(tmp_storage, sess->session_id, sess->session_id_length); + session_id = tmp_storage; + } + + uint32_t hash = + ((uint32_t)session_id[0]) | + ((uint32_t)session_id[1] << 8) | + ((uint32_t)session_id[2] << 16) | + ((uint32_t)session_id[3] << 24); + + return hash; +} + +/* NB: If this function (or indeed the hash function which uses a sort of + * coarser function than this one) is changed, ensure + * SSL_CTX_has_matching_session_id() is checked accordingly. It relies on being + * able to construct an SSL_SESSION that will collide with any existing session + * with a matching session ID. */ +static int ssl_session_cmp(const SSL_SESSION *a, const SSL_SESSION *b) { + if (a->ssl_version != b->ssl_version) { + return 1; + } + + if (a->session_id_length != b->session_id_length) { + return 1; + } + + return OPENSSL_memcmp(a->session_id, b->session_id, a->session_id_length); +} + +SSL_CTX *SSL_CTX_new(const SSL_METHOD *method) { + SSL_CTX *ret = NULL; + + if (method == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NULL_SSL_METHOD_PASSED); + return NULL; + } + + ret = OPENSSL_malloc(sizeof(SSL_CTX)); + if (ret == NULL) { + goto err; + } + + OPENSSL_memset(ret, 0, sizeof(SSL_CTX)); + + ret->method = method->method; + ret->x509_method = method->x509_method; + + CRYPTO_MUTEX_init(&ret->lock); + + ret->session_cache_mode = SSL_SESS_CACHE_SERVER; + ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT; + + ret->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT; + ret->session_psk_dhe_timeout = SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT; + + ret->references = 1; + + ret->max_cert_list = SSL_MAX_CERT_LIST_DEFAULT; + ret->verify_mode = SSL_VERIFY_NONE; + ret->cert = ssl_cert_new(method->x509_method); + if (ret->cert == NULL) { + goto err; + } + + ret->sessions = lh_SSL_SESSION_new(ssl_session_hash, ssl_session_cmp); + if (ret->sessions == NULL) { + goto err; + } + + if (!ret->x509_method->ssl_ctx_new(ret)) { + goto err; + } + + if (!SSL_CTX_set_strict_cipher_list(ret, SSL_DEFAULT_CIPHER_LIST)) { + goto err2; + } + + ret->client_CA = sk_CRYPTO_BUFFER_new_null(); + if (ret->client_CA == NULL) { + goto err; + } + + CRYPTO_new_ex_data(&ret->ex_data); + + ret->max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH; + + /* Setup RFC4507 ticket keys */ + if (!RAND_bytes(ret->tlsext_tick_key_name, 16) || + !RAND_bytes(ret->tlsext_tick_hmac_key, 16) || + !RAND_bytes(ret->tlsext_tick_aes_key, 16)) { + ret->options |= SSL_OP_NO_TICKET; + } + + /* Disable the auto-chaining feature by default. Once this has stuck without + * problems, the feature will be removed entirely. */ + ret->mode = SSL_MODE_NO_AUTO_CHAIN; + + /* Lock the SSL_CTX to the specified version, for compatibility with legacy + * uses of SSL_METHOD. */ + if (!SSL_CTX_set_max_proto_version(ret, method->version) || + !SSL_CTX_set_min_proto_version(ret, method->version)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err2; + } + + return ret; + +err: + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); +err2: + SSL_CTX_free(ret); + return NULL; +} + +int SSL_CTX_up_ref(SSL_CTX *ctx) { + CRYPTO_refcount_inc(&ctx->references); + return 1; +} + +void SSL_CTX_free(SSL_CTX *ctx) { + if (ctx == NULL || + !CRYPTO_refcount_dec_and_test_zero(&ctx->references)) { + return; + } + + /* Free internal session cache. However: the remove_cb() may reference the + * ex_data of SSL_CTX, thus the ex_data store can only be removed after the + * sessions were flushed. As the ex_data handling routines might also touch + * the session cache, the most secure solution seems to be: empty (flush) the + * cache, then free ex_data, then finally free the cache. (See ticket + * [openssl.org #212].) */ + SSL_CTX_flush_sessions(ctx, 0); + + CRYPTO_free_ex_data(&g_ex_data_class_ssl_ctx, ctx, &ctx->ex_data); + + CRYPTO_MUTEX_cleanup(&ctx->lock); + lh_SSL_SESSION_free(ctx->sessions); + ssl_cipher_preference_list_free(ctx->cipher_list); + ssl_cert_free(ctx->cert); + sk_SSL_CUSTOM_EXTENSION_pop_free(ctx->client_custom_extensions, + SSL_CUSTOM_EXTENSION_free); + sk_SSL_CUSTOM_EXTENSION_pop_free(ctx->server_custom_extensions, + SSL_CUSTOM_EXTENSION_free); + sk_CRYPTO_BUFFER_pop_free(ctx->client_CA, CRYPTO_BUFFER_free); + ctx->x509_method->ssl_ctx_free(ctx); + sk_SRTP_PROTECTION_PROFILE_free(ctx->srtp_profiles); + OPENSSL_free(ctx->psk_identity_hint); + OPENSSL_free(ctx->supported_group_list); + OPENSSL_free(ctx->alpn_client_proto_list); + EVP_PKEY_free(ctx->tlsext_channel_id_private); + + OPENSSL_free(ctx); +} + +SSL *SSL_new(SSL_CTX *ctx) { + if (ctx == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NULL_SSL_CTX); + return NULL; + } + if (ctx->method == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION); + return NULL; + } + + SSL *ssl = OPENSSL_malloc(sizeof(SSL)); + if (ssl == NULL) { + goto err; + } + OPENSSL_memset(ssl, 0, sizeof(SSL)); + + ssl->min_version = ctx->min_version; + ssl->max_version = ctx->max_version; + + /* RFC 6347 states that implementations SHOULD use an initial timer value of + * 1 second. */ + ssl->initial_timeout_duration_ms = 1000; + + ssl->options = ctx->options; + ssl->mode = ctx->mode; + ssl->max_cert_list = ctx->max_cert_list; + + ssl->cert = ssl_cert_dup(ctx->cert); + if (ssl->cert == NULL) { + goto err; + } + + ssl->msg_callback = ctx->msg_callback; + ssl->msg_callback_arg = ctx->msg_callback_arg; + ssl->verify_mode = ctx->verify_mode; + ssl->verify_callback = ctx->default_verify_callback; + ssl->retain_only_sha256_of_client_certs = + ctx->retain_only_sha256_of_client_certs; + + ssl->quiet_shutdown = ctx->quiet_shutdown; + ssl->max_send_fragment = ctx->max_send_fragment; + + SSL_CTX_up_ref(ctx); + ssl->ctx = ctx; + SSL_CTX_up_ref(ctx); + ssl->session_ctx = ctx; + + if (!ssl->ctx->x509_method->ssl_new(ssl)) { + goto err; + } + + if (ctx->supported_group_list) { + ssl->supported_group_list = BUF_memdup(ctx->supported_group_list, + ctx->supported_group_list_len * 2); + if (!ssl->supported_group_list) { + goto err; + } + ssl->supported_group_list_len = ctx->supported_group_list_len; + } + + if (ctx->alpn_client_proto_list) { + ssl->alpn_client_proto_list = BUF_memdup(ctx->alpn_client_proto_list, + ctx->alpn_client_proto_list_len); + if (ssl->alpn_client_proto_list == NULL) { + goto err; + } + ssl->alpn_client_proto_list_len = ctx->alpn_client_proto_list_len; + } + + ssl->method = ctx->method; + + if (!ssl->method->ssl_new(ssl)) { + goto err; + } + + ssl->rwstate = SSL_NOTHING; + + CRYPTO_new_ex_data(&ssl->ex_data); + + ssl->psk_identity_hint = NULL; + if (ctx->psk_identity_hint) { + ssl->psk_identity_hint = BUF_strdup(ctx->psk_identity_hint); + if (ssl->psk_identity_hint == NULL) { + goto err; + } + } + ssl->psk_client_callback = ctx->psk_client_callback; + ssl->psk_server_callback = ctx->psk_server_callback; + + ssl->tlsext_channel_id_enabled = ctx->tlsext_channel_id_enabled; + if (ctx->tlsext_channel_id_private) { + EVP_PKEY_up_ref(ctx->tlsext_channel_id_private); + ssl->tlsext_channel_id_private = ctx->tlsext_channel_id_private; + } + + ssl->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled; + ssl->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled; + + return ssl; + +err: + SSL_free(ssl); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + + return NULL; +} + +void SSL_free(SSL *ssl) { + if (ssl == NULL) { + return; + } + + ssl->ctx->x509_method->ssl_free(ssl); + CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data); + + BIO_free_all(ssl->rbio); + BIO_free_all(ssl->wbio); + + BUF_MEM_free(ssl->init_buf); + + /* add extra stuff */ + ssl_cipher_preference_list_free(ssl->cipher_list); + + SSL_SESSION_free(ssl->session); + + ssl_cert_free(ssl->cert); + + OPENSSL_free(ssl->tlsext_hostname); + SSL_CTX_free(ssl->session_ctx); + OPENSSL_free(ssl->supported_group_list); + OPENSSL_free(ssl->alpn_client_proto_list); + EVP_PKEY_free(ssl->tlsext_channel_id_private); + OPENSSL_free(ssl->psk_identity_hint); + sk_CRYPTO_BUFFER_pop_free(ssl->client_CA, CRYPTO_BUFFER_free); + sk_SRTP_PROTECTION_PROFILE_free(ssl->srtp_profiles); + + if (ssl->method != NULL) { + ssl->method->ssl_free(ssl); + } + SSL_CTX_free(ssl->ctx); + + OPENSSL_free(ssl); +} + +void SSL_set_connect_state(SSL *ssl) { + ssl->server = 0; + ssl->handshake_func = ssl3_connect; +} + +void SSL_set_accept_state(SSL *ssl) { + ssl->server = 1; + ssl->handshake_func = ssl3_accept; +} + +void SSL_set0_rbio(SSL *ssl, BIO *rbio) { + BIO_free_all(ssl->rbio); + ssl->rbio = rbio; +} + +void SSL_set0_wbio(SSL *ssl, BIO *wbio) { + BIO_free_all(ssl->wbio); + ssl->wbio = wbio; +} + +void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) { + /* For historical reasons, this function has many different cases in ownership + * handling. */ + + /* If nothing has changed, do nothing */ + if (rbio == SSL_get_rbio(ssl) && wbio == SSL_get_wbio(ssl)) { + return; + } + + /* If the two arguments are equal, one fewer reference is granted than + * taken. */ + if (rbio != NULL && rbio == wbio) { + BIO_up_ref(rbio); + } + + /* If only the wbio is changed, adopt only one reference. */ + if (rbio == SSL_get_rbio(ssl)) { + SSL_set0_wbio(ssl, wbio); + return; + } + + /* There is an asymmetry here for historical reasons. If only the rbio is + * changed AND the rbio and wbio were originally different, then we only adopt + * one reference. */ + if (wbio == SSL_get_wbio(ssl) && SSL_get_rbio(ssl) != SSL_get_wbio(ssl)) { + SSL_set0_rbio(ssl, rbio); + return; + } + + /* Otherwise, adopt both references. */ + SSL_set0_rbio(ssl, rbio); + SSL_set0_wbio(ssl, wbio); +} + +BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio; } + +BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio; } + +void ssl_reset_error_state(SSL *ssl) { + /* Functions which use |SSL_get_error| must reset I/O and error state on + * entry. */ + ssl->rwstate = SSL_NOTHING; + ERR_clear_error(); + ERR_clear_system_error(); +} + +int SSL_do_handshake(SSL *ssl) { + ssl_reset_error_state(ssl); + + if (ssl->handshake_func == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_TYPE_NOT_SET); + return -1; + } + + if (!SSL_in_init(ssl)) { + return 1; + } + + if (ssl->s3->hs == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* Run the handshake. */ + assert(ssl->s3->hs != NULL); + int ret = ssl->handshake_func(ssl->s3->hs); + if (ret <= 0) { + return ret; + } + + /* Destroy the handshake object if the handshake has completely finished. */ + if (!SSL_in_init(ssl)) { + ssl_handshake_free(ssl->s3->hs); + ssl->s3->hs = NULL; + } + + return 1; +} + +int SSL_connect(SSL *ssl) { + if (ssl->handshake_func == NULL) { + /* Not properly initialized yet */ + SSL_set_connect_state(ssl); + } + + return SSL_do_handshake(ssl); +} + +int SSL_accept(SSL *ssl) { + if (ssl->handshake_func == NULL) { + /* Not properly initialized yet */ + SSL_set_accept_state(ssl); + } + + return SSL_do_handshake(ssl); +} + +static int ssl_do_renegotiate(SSL *ssl) { + /* We do not accept renegotiations as a server or SSL 3.0. SSL 3.0 will be + * removed entirely in the future and requires retaining more data for + * renegotiation_info. */ + if (ssl->server || ssl->version == SSL3_VERSION) { + goto no_renegotiation; + } + + if (ssl->s3->tmp.message_type != SSL3_MT_HELLO_REQUEST || + ssl->init_num != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HELLO_REQUEST); + return 0; + } + + switch (ssl->renegotiate_mode) { + case ssl_renegotiate_ignore: + /* Ignore the HelloRequest. */ + return 1; + + case ssl_renegotiate_once: + if (ssl->s3->total_renegotiations != 0) { + goto no_renegotiation; + } + break; + + case ssl_renegotiate_never: + goto no_renegotiation; + + case ssl_renegotiate_freely: + break; + } + + /* Renegotiation is only supported at quiescent points in the application + * protocol, namely in HTTPS, just before reading the HTTP response. Require + * the record-layer be idle and avoid complexities of sending a handshake + * record while an application_data record is being written. */ + if (ssl_write_buffer_is_pending(ssl)) { + goto no_renegotiation; + } + + /* Begin a new handshake. */ + if (ssl->s3->hs != NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + ssl->s3->hs = ssl_handshake_new(ssl); + if (ssl->s3->hs == NULL) { + return 0; + } + + ssl->s3->total_renegotiations++; + return 1; + +no_renegotiation: + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_NO_RENEGOTIATION); + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_RENEGOTIATION); + return 0; +} + +static int ssl_do_post_handshake(SSL *ssl) { + if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + return ssl_do_renegotiate(ssl); + } + + return tls13_post_handshake(ssl); +} + +static int ssl_read_impl(SSL *ssl, void *buf, int num, int peek) { + ssl_reset_error_state(ssl); + + if (ssl->handshake_func == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED); + return -1; + } + + for (;;) { + /* Complete the current handshake, if any. False Start will cause + * |SSL_do_handshake| to return mid-handshake, so this may require multiple + * iterations. */ + while (SSL_in_init(ssl)) { + int ret = SSL_do_handshake(ssl); + if (ret < 0) { + return ret; + } + if (ret == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + + int got_handshake; + int ret = ssl->method->read_app_data(ssl, &got_handshake, buf, num, peek); + if (ret > 0 || !got_handshake) { + ssl->s3->key_update_count = 0; + return ret; + } + + /* Handle the post-handshake message and try again. */ + if (!ssl_do_post_handshake(ssl)) { + return -1; + } + ssl->method->release_current_message(ssl, 1 /* free buffer */); + } +} + +int SSL_read(SSL *ssl, void *buf, int num) { + return ssl_read_impl(ssl, buf, num, 0 /* consume bytes */); +} + +int SSL_peek(SSL *ssl, void *buf, int num) { + return ssl_read_impl(ssl, buf, num, 1 /* peek */); +} + +int SSL_write(SSL *ssl, const void *buf, int num) { + ssl_reset_error_state(ssl); + + if (ssl->handshake_func == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED); + return -1; + } + + if (ssl->s3->send_shutdown != ssl_shutdown_none) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + + /* If necessary, complete the handshake implicitly. */ + if (SSL_in_init(ssl) && !SSL_in_false_start(ssl)) { + int ret = SSL_do_handshake(ssl); + if (ret < 0) { + return ret; + } + if (ret == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + + return ssl->method->write_app_data(ssl, buf, num); +} + +int SSL_shutdown(SSL *ssl) { + ssl_reset_error_state(ssl); + + if (ssl->handshake_func == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED); + return -1; + } + + /* If we are in the middle of a handshake, silently succeed. Consumers often + * call this function before |SSL_free|, whether the handshake succeeded or + * not. We assume the caller has already handled failed handshakes. */ + if (SSL_in_init(ssl)) { + return 1; + } + + if (ssl->quiet_shutdown) { + /* Do nothing if configured not to send a close_notify. */ + ssl->s3->send_shutdown = ssl_shutdown_close_notify; + ssl->s3->recv_shutdown = ssl_shutdown_close_notify; + return 1; + } + + /* This function completes in two stages. It sends a close_notify and then it + * waits for a close_notify to come in. Perform exactly one action and return + * whether or not it succeeds. */ + + if (ssl->s3->send_shutdown != ssl_shutdown_close_notify) { + /* Send a close_notify. */ + if (ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY) <= 0) { + return -1; + } + } else if (ssl->s3->alert_dispatch) { + /* Finish sending the close_notify. */ + if (ssl->method->dispatch_alert(ssl) <= 0) { + return -1; + } + } else if (ssl->s3->recv_shutdown != ssl_shutdown_close_notify) { + /* Wait for the peer's close_notify. */ + ssl->method->read_close_notify(ssl); + if (ssl->s3->recv_shutdown != ssl_shutdown_close_notify) { + return -1; + } + } + + /* Return 0 for unidirectional shutdown and 1 for bidirectional shutdown. */ + return ssl->s3->recv_shutdown == ssl_shutdown_close_notify; +} + +int SSL_send_fatal_alert(SSL *ssl, uint8_t alert) { + if (ssl->s3->alert_dispatch) { + if (ssl->s3->send_alert[0] != SSL3_AL_FATAL || + ssl->s3->send_alert[1] != alert) { + /* We are already attempting to write a different alert. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + return ssl->method->dispatch_alert(ssl); + } + + return ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); +} + +void SSL_CTX_set_early_data_enabled(SSL_CTX *ctx, int enabled) { + ctx->enable_early_data = !!enabled; +} + +static int bio_retry_reason_to_error(int reason) { + switch (reason) { + case BIO_RR_CONNECT: + return SSL_ERROR_WANT_CONNECT; + case BIO_RR_ACCEPT: + return SSL_ERROR_WANT_ACCEPT; + default: + return SSL_ERROR_SYSCALL; + } +} + +int SSL_get_error(const SSL *ssl, int ret_code) { + if (ret_code > 0) { + return SSL_ERROR_NONE; + } + + /* Make things return SSL_ERROR_SYSCALL when doing SSL_do_handshake etc, + * where we do encode the error */ + uint32_t err = ERR_peek_error(); + if (err != 0) { + if (ERR_GET_LIB(err) == ERR_LIB_SYS) { + return SSL_ERROR_SYSCALL; + } + return SSL_ERROR_SSL; + } + + if (ret_code == 0) { + if (ssl->s3->recv_shutdown == ssl_shutdown_close_notify) { + return SSL_ERROR_ZERO_RETURN; + } + /* An EOF was observed which violates the protocol, and the underlying + * transport does not participate in the error queue. Bubble up to the + * caller. */ + return SSL_ERROR_SYSCALL; + } + + switch (ssl->rwstate) { + case SSL_PENDING_SESSION: + return SSL_ERROR_PENDING_SESSION; + + case SSL_CERTIFICATE_SELECTION_PENDING: + return SSL_ERROR_PENDING_CERTIFICATE; + + case SSL_READING: { + BIO *bio = SSL_get_rbio(ssl); + if (BIO_should_read(bio)) { + return SSL_ERROR_WANT_READ; + } + + if (BIO_should_write(bio)) { + /* TODO(davidben): OpenSSL historically checked for writes on the read + * BIO. Can this be removed? */ + return SSL_ERROR_WANT_WRITE; + } + + if (BIO_should_io_special(bio)) { + return bio_retry_reason_to_error(BIO_get_retry_reason(bio)); + } + + break; + } + + case SSL_WRITING: { + BIO *bio = SSL_get_wbio(ssl); + if (BIO_should_write(bio)) { + return SSL_ERROR_WANT_WRITE; + } + + if (BIO_should_read(bio)) { + /* TODO(davidben): OpenSSL historically checked for reads on the write + * BIO. Can this be removed? */ + return SSL_ERROR_WANT_READ; + } + + if (BIO_should_io_special(bio)) { + return bio_retry_reason_to_error(BIO_get_retry_reason(bio)); + } + + break; + } + + case SSL_X509_LOOKUP: + return SSL_ERROR_WANT_X509_LOOKUP; + + case SSL_CHANNEL_ID_LOOKUP: + return SSL_ERROR_WANT_CHANNEL_ID_LOOKUP; + + case SSL_PRIVATE_KEY_OPERATION: + return SSL_ERROR_WANT_PRIVATE_KEY_OPERATION; + + case SSL_PENDING_TICKET: + return SSL_ERROR_PENDING_TICKET; + } + + return SSL_ERROR_SYSCALL; +} + +static int set_min_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out, + uint16_t version) { + if (version == 0) { + *out = method->min_version; + return 1; + } + + if (version == TLS1_3_VERSION) { + version = TLS1_3_DRAFT_VERSION; + } + + return method->version_from_wire(out, version); +} + +static int set_max_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out, + uint16_t version) { + if (version == 0) { + *out = method->max_version; + /* TODO(svaldez): Enable TLS 1.3 by default once fully implemented. */ + if (*out > TLS1_2_VERSION) { + *out = TLS1_2_VERSION; + } + return 1; + } + + if (version == TLS1_3_VERSION) { + version = TLS1_3_DRAFT_VERSION; + } + + return method->version_from_wire(out, version); +} + +int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) { + return set_min_version(ctx->method, &ctx->min_version, version); +} + +int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) { + return set_max_version(ctx->method, &ctx->max_version, version); +} + +int SSL_set_min_proto_version(SSL *ssl, uint16_t version) { + return set_min_version(ssl->method, &ssl->min_version, version); +} + +int SSL_set_max_proto_version(SSL *ssl, uint16_t version) { + return set_max_version(ssl->method, &ssl->max_version, version); +} + +uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) { + ctx->options |= options; + return ctx->options; +} + +uint32_t SSL_CTX_clear_options(SSL_CTX *ctx, uint32_t options) { + ctx->options &= ~options; + return ctx->options; +} + +uint32_t SSL_CTX_get_options(const SSL_CTX *ctx) { return ctx->options; } + +uint32_t SSL_set_options(SSL *ssl, uint32_t options) { + ssl->options |= options; + return ssl->options; +} + +uint32_t SSL_clear_options(SSL *ssl, uint32_t options) { + ssl->options &= ~options; + return ssl->options; +} + +uint32_t SSL_get_options(const SSL *ssl) { return ssl->options; } + +uint32_t SSL_CTX_set_mode(SSL_CTX *ctx, uint32_t mode) { + ctx->mode |= mode; + return ctx->mode; +} + +uint32_t SSL_CTX_clear_mode(SSL_CTX *ctx, uint32_t mode) { + ctx->mode &= ~mode; + return ctx->mode; +} + +uint32_t SSL_CTX_get_mode(const SSL_CTX *ctx) { return ctx->mode; } + +uint32_t SSL_set_mode(SSL *ssl, uint32_t mode) { + ssl->mode |= mode; + return ssl->mode; +} + +uint32_t SSL_clear_mode(SSL *ssl, uint32_t mode) { + ssl->mode &= ~mode; + return ssl->mode; +} + +uint32_t SSL_get_mode(const SSL *ssl) { return ssl->mode; } + +void SSL_CTX_set0_buffer_pool(SSL_CTX *ctx, CRYPTO_BUFFER_POOL *pool) { + ctx->pool = pool; +} + +int SSL_get_tls_unique(const SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out) { + /* tls-unique is not defined for SSL 3.0 or TLS 1.3. */ + if (!ssl->s3->initial_handshake_complete || + ssl3_protocol_version(ssl) < TLS1_VERSION || + ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + goto err; + } + + /* The tls-unique value is the first Finished message in the handshake, which + * is the client's in a full handshake and the server's for a resumption. See + * https://tools.ietf.org/html/rfc5929#section-3.1. */ + const uint8_t *finished = ssl->s3->previous_client_finished; + size_t finished_len = ssl->s3->previous_client_finished_len; + if (ssl->session != NULL) { + /* tls-unique is broken for resumed sessions unless EMS is used. */ + if (!ssl->session->extended_master_secret) { + goto err; + } + finished = ssl->s3->previous_server_finished; + finished_len = ssl->s3->previous_server_finished_len; + } + + *out_len = finished_len; + if (finished_len > max_out) { + *out_len = max_out; + } + + OPENSSL_memcpy(out, finished, *out_len); + return 1; + +err: + *out_len = 0; + OPENSSL_memset(out, 0, max_out); + return 0; +} + +static int set_session_id_context(CERT *cert, const uint8_t *sid_ctx, + size_t sid_ctx_len) { + if (sid_ctx_len > sizeof(cert->sid_ctx)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); + return 0; + } + + OPENSSL_COMPILE_ASSERT(sizeof(cert->sid_ctx) < 256, sid_ctx_too_large); + cert->sid_ctx_length = (uint8_t)sid_ctx_len; + OPENSSL_memcpy(cert->sid_ctx, sid_ctx, sid_ctx_len); + return 1; +} + +int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const uint8_t *sid_ctx, + size_t sid_ctx_len) { + return set_session_id_context(ctx->cert, sid_ctx, sid_ctx_len); +} + +int SSL_set_session_id_context(SSL *ssl, const uint8_t *sid_ctx, + size_t sid_ctx_len) { + return set_session_id_context(ssl->cert, sid_ctx, sid_ctx_len); +} + +const uint8_t *SSL_get0_session_id_context(const SSL *ssl, size_t *out_len) { + *out_len = ssl->cert->sid_ctx_length; + return ssl->cert->sid_ctx; +} + +void ssl_cipher_preference_list_free( + struct ssl_cipher_preference_list_st *cipher_list) { + if (cipher_list == NULL) { + return; + } + sk_SSL_CIPHER_free(cipher_list->ciphers); + OPENSSL_free(cipher_list->in_group_flags); + OPENSSL_free(cipher_list); +} + +void SSL_certs_clear(SSL *ssl) { ssl_cert_clear_certs(ssl->cert); } + +int SSL_get_fd(const SSL *ssl) { return SSL_get_rfd(ssl); } + +int SSL_get_rfd(const SSL *ssl) { + int ret = -1; + BIO *b = BIO_find_type(SSL_get_rbio(ssl), BIO_TYPE_DESCRIPTOR); + if (b != NULL) { + BIO_get_fd(b, &ret); + } + return ret; +} + +int SSL_get_wfd(const SSL *ssl) { + int ret = -1; + BIO *b = BIO_find_type(SSL_get_wbio(ssl), BIO_TYPE_DESCRIPTOR); + if (b != NULL) { + BIO_get_fd(b, &ret); + } + return ret; +} + +int SSL_set_fd(SSL *ssl, int fd) { + BIO *bio = BIO_new(BIO_s_socket()); + if (bio == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + return 0; + } + BIO_set_fd(bio, fd, BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + return 1; +} + +int SSL_set_wfd(SSL *ssl, int fd) { + BIO *rbio = SSL_get_rbio(ssl); + if (rbio == NULL || BIO_method_type(rbio) != BIO_TYPE_SOCKET || + BIO_get_fd(rbio, NULL) != fd) { + BIO *bio = BIO_new(BIO_s_socket()); + if (bio == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + return 0; + } + BIO_set_fd(bio, fd, BIO_NOCLOSE); + SSL_set0_wbio(ssl, bio); + } else { + /* Copy the rbio over to the wbio. */ + BIO_up_ref(rbio); + SSL_set0_wbio(ssl, rbio); + } + + return 1; +} + +int SSL_set_rfd(SSL *ssl, int fd) { + BIO *wbio = SSL_get_wbio(ssl); + if (wbio == NULL || BIO_method_type(wbio) != BIO_TYPE_SOCKET || + BIO_get_fd(wbio, NULL) != fd) { + BIO *bio = BIO_new(BIO_s_socket()); + if (bio == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB); + return 0; + } + BIO_set_fd(bio, fd, BIO_NOCLOSE); + SSL_set0_rbio(ssl, bio); + } else { + /* Copy the wbio over to the rbio. */ + BIO_up_ref(wbio); + SSL_set0_rbio(ssl, wbio); + } + return 1; +} + +static size_t copy_finished(void *out, size_t out_len, const uint8_t *in, + size_t in_len) { + if (out_len > in_len) { + out_len = in_len; + } + OPENSSL_memcpy(out, in, out_len); + return in_len; +} + +size_t SSL_get_finished(const SSL *ssl, void *buf, size_t count) { + if (!ssl->s3->initial_handshake_complete || + ssl3_protocol_version(ssl) < TLS1_VERSION || + ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 0; + } + + if (ssl->server) { + return copy_finished(buf, count, ssl->s3->previous_server_finished, + ssl->s3->previous_server_finished_len); + } + + return copy_finished(buf, count, ssl->s3->previous_client_finished, + ssl->s3->previous_client_finished_len); +} + +size_t SSL_get_peer_finished(const SSL *ssl, void *buf, size_t count) { + if (!ssl->s3->initial_handshake_complete || + ssl3_protocol_version(ssl) < TLS1_VERSION || + ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 0; + } + + if (ssl->server) { + return copy_finished(buf, count, ssl->s3->previous_client_finished, + ssl->s3->previous_client_finished_len); + } + + return copy_finished(buf, count, ssl->s3->previous_server_finished, + ssl->s3->previous_server_finished_len); +} + +int SSL_get_verify_mode(const SSL *ssl) { return ssl->verify_mode; } + +int SSL_get_extms_support(const SSL *ssl) { + /* TLS 1.3 does not require extended master secret and always reports as + * supporting it. */ + if (!ssl->s3->have_version) { + return 0; + } + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 1; + } + + /* If the initial handshake completed, query the established session. */ + if (ssl->s3->established_session != NULL) { + return ssl->s3->established_session->extended_master_secret; + } + + /* Otherwise, query the in-progress handshake. */ + if (ssl->s3->hs != NULL) { + return ssl->s3->hs->extended_master_secret; + } + assert(0); + return 0; +} + +int SSL_CTX_get_read_ahead(const SSL_CTX *ctx) { return 0; } + +int SSL_get_read_ahead(const SSL *ssl) { return 0; } + +void SSL_CTX_set_read_ahead(SSL_CTX *ctx, int yes) { } + +void SSL_set_read_ahead(SSL *ssl, int yes) { } + +int SSL_pending(const SSL *ssl) { + if (ssl->s3->rrec.type != SSL3_RT_APPLICATION_DATA) { + return 0; + } + return ssl->s3->rrec.length; +} + +/* Fix this so it checks all the valid key/cert options */ +int SSL_CTX_check_private_key(const SSL_CTX *ctx) { + return ssl_cert_check_private_key(ctx->cert, ctx->cert->privatekey); +} + +/* Fix this function so that it takes an optional type parameter */ +int SSL_check_private_key(const SSL *ssl) { + return ssl_cert_check_private_key(ssl->cert, ssl->cert->privatekey); +} + +long SSL_get_default_timeout(const SSL *ssl) { + return SSL_DEFAULT_SESSION_TIMEOUT; +} + +int SSL_renegotiate(SSL *ssl) { + /* Caller-initiated renegotiation is not supported. */ + OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; +} + +int SSL_renegotiate_pending(SSL *ssl) { + return SSL_in_init(ssl) && ssl->s3->initial_handshake_complete; +} + +int SSL_total_renegotiations(const SSL *ssl) { + return ssl->s3->total_renegotiations; +} + +size_t SSL_CTX_get_max_cert_list(const SSL_CTX *ctx) { + return ctx->max_cert_list; +} + +void SSL_CTX_set_max_cert_list(SSL_CTX *ctx, size_t max_cert_list) { + if (max_cert_list > kMaxHandshakeSize) { + max_cert_list = kMaxHandshakeSize; + } + ctx->max_cert_list = (uint32_t)max_cert_list; +} + +size_t SSL_get_max_cert_list(const SSL *ssl) { + return ssl->max_cert_list; +} + +void SSL_set_max_cert_list(SSL *ssl, size_t max_cert_list) { + if (max_cert_list > kMaxHandshakeSize) { + max_cert_list = kMaxHandshakeSize; + } + ssl->max_cert_list = (uint32_t)max_cert_list; +} + +int SSL_CTX_set_max_send_fragment(SSL_CTX *ctx, size_t max_send_fragment) { + if (max_send_fragment < 512) { + max_send_fragment = 512; + } + if (max_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) { + max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH; + } + ctx->max_send_fragment = (uint16_t)max_send_fragment; + + return 1; +} + +int SSL_set_max_send_fragment(SSL *ssl, size_t max_send_fragment) { + if (max_send_fragment < 512) { + max_send_fragment = 512; + } + if (max_send_fragment > SSL3_RT_MAX_PLAIN_LENGTH) { + max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH; + } + ssl->max_send_fragment = (uint16_t)max_send_fragment; + + return 1; +} + +int SSL_set_mtu(SSL *ssl, unsigned mtu) { + if (!SSL_is_dtls(ssl) || mtu < dtls1_min_mtu()) { + return 0; + } + ssl->d1->mtu = mtu; + return 1; +} + +int SSL_get_secure_renegotiation_support(const SSL *ssl) { + if (!ssl->s3->have_version) { + return 0; + } + return ssl3_protocol_version(ssl) >= TLS1_3_VERSION || + ssl->s3->send_connection_binding; +} + +LHASH_OF(SSL_SESSION) *SSL_CTX_sessions(SSL_CTX *ctx) { return ctx->sessions; } + +size_t SSL_CTX_sess_number(const SSL_CTX *ctx) { + return lh_SSL_SESSION_num_items(ctx->sessions); +} + +unsigned long SSL_CTX_sess_set_cache_size(SSL_CTX *ctx, unsigned long size) { + unsigned long ret = ctx->session_cache_size; + ctx->session_cache_size = size; + return ret; +} + +unsigned long SSL_CTX_sess_get_cache_size(const SSL_CTX *ctx) { + return ctx->session_cache_size; +} + +int SSL_CTX_set_session_cache_mode(SSL_CTX *ctx, int mode) { + int ret = ctx->session_cache_mode; + ctx->session_cache_mode = mode; + return ret; +} + +int SSL_CTX_get_session_cache_mode(const SSL_CTX *ctx) { + return ctx->session_cache_mode; +} + + +int SSL_CTX_get_tlsext_ticket_keys(SSL_CTX *ctx, void *out, size_t len) { + if (out == NULL) { + return 48; + } + if (len != 48) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH); + return 0; + } + uint8_t *out_bytes = out; + OPENSSL_memcpy(out_bytes, ctx->tlsext_tick_key_name, 16); + OPENSSL_memcpy(out_bytes + 16, ctx->tlsext_tick_hmac_key, 16); + OPENSSL_memcpy(out_bytes + 32, ctx->tlsext_tick_aes_key, 16); + return 1; +} + +int SSL_CTX_set_tlsext_ticket_keys(SSL_CTX *ctx, const void *in, size_t len) { + if (in == NULL) { + return 48; + } + if (len != 48) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH); + return 0; + } + const uint8_t *in_bytes = in; + OPENSSL_memcpy(ctx->tlsext_tick_key_name, in_bytes, 16); + OPENSSL_memcpy(ctx->tlsext_tick_hmac_key, in_bytes + 16, 16); + OPENSSL_memcpy(ctx->tlsext_tick_aes_key, in_bytes + 32, 16); + return 1; +} + +int SSL_CTX_set_tlsext_ticket_key_cb( + SSL_CTX *ctx, int (*callback)(SSL *ssl, uint8_t *key_name, uint8_t *iv, + EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, + int encrypt)) { + ctx->tlsext_ticket_key_cb = callback; + return 1; +} + +int SSL_CTX_set1_curves(SSL_CTX *ctx, const int *curves, size_t curves_len) { + return tls1_set_curves(&ctx->supported_group_list, + &ctx->supported_group_list_len, curves, + curves_len); +} + +int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) { + return tls1_set_curves(&ssl->supported_group_list, + &ssl->supported_group_list_len, curves, + curves_len); +} + +int SSL_CTX_set1_curves_list(SSL_CTX *ctx, const char *curves) { + return tls1_set_curves_list(&ctx->supported_group_list, + &ctx->supported_group_list_len, curves); +} + +int SSL_set1_curves_list(SSL *ssl, const char *curves) { + return tls1_set_curves_list(&ssl->supported_group_list, + &ssl->supported_group_list_len, curves); +} + +uint16_t SSL_get_curve_id(const SSL *ssl) { + /* TODO(davidben): This checks the wrong session if there is a renegotiation in + * progress. */ + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL) { + return 0; + } + + return session->group_id; +} + +int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh) { + DH_free(ctx->cert->dh_tmp); + ctx->cert->dh_tmp = DHparams_dup(dh); + if (ctx->cert->dh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); + return 0; + } + return 1; +} + +int SSL_set_tmp_dh(SSL *ssl, const DH *dh) { + DH_free(ssl->cert->dh_tmp); + ssl->cert->dh_tmp = DHparams_dup(dh); + if (ssl->cert->dh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB); + return 0; + } + return 1; +} + +OPENSSL_EXPORT STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx) { + return ctx->cipher_list->ciphers; +} + +STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *ssl) { + if (ssl == NULL) { + return NULL; + } + + const struct ssl_cipher_preference_list_st *prefs = + ssl_get_cipher_preferences(ssl); + if (prefs == NULL) { + return NULL; + } + + return prefs->ciphers; +} + +const char *SSL_get_cipher_list(const SSL *ssl, int n) { + if (ssl == NULL) { + return NULL; + } + + STACK_OF(SSL_CIPHER) *sk = SSL_get_ciphers(ssl); + if (sk == NULL || n < 0 || (size_t)n >= sk_SSL_CIPHER_num(sk)) { + return NULL; + } + + const SSL_CIPHER *c = sk_SSL_CIPHER_value(sk, n); + if (c == NULL) { + return NULL; + } + + return c->name; +} + +int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) { + return ssl_create_cipher_list(ctx->method, &ctx->cipher_list, str, + 0 /* not strict */); +} + +int SSL_CTX_set_strict_cipher_list(SSL_CTX *ctx, const char *str) { + return ssl_create_cipher_list(ctx->method, &ctx->cipher_list, str, + 1 /* strict */); +} + +int SSL_set_cipher_list(SSL *ssl, const char *str) { + return ssl_create_cipher_list(ssl->ctx->method, &ssl->cipher_list, str, + 0 /* not strict */); +} + +int SSL_set_strict_cipher_list(SSL *ssl, const char *str) { + return ssl_create_cipher_list(ssl->ctx->method, &ssl->cipher_list, str, + 1 /* strict */); +} + +const char *SSL_get_servername(const SSL *ssl, const int type) { + if (type != TLSEXT_NAMETYPE_host_name) { + return NULL; + } + + /* Historically, |SSL_get_servername| was also the configuration getter + * corresponding to |SSL_set_tlsext_host_name|. */ + if (ssl->tlsext_hostname != NULL) { + return ssl->tlsext_hostname; + } + + /* During the handshake, report the handshake value. */ + if (ssl->s3->hs != NULL) { + return ssl->s3->hs->hostname; + } + + /* SSL_get_servername may also be called after the handshake to look up the + * SNI value. + * + * TODO(davidben): This is almost unused. Can we remove it? */ + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL) { + return NULL; + } + return session->tlsext_hostname; +} + +int SSL_get_servername_type(const SSL *ssl) { + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL || session->tlsext_hostname == NULL) { + return -1; + } + return TLSEXT_NAMETYPE_host_name; +} + +void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) { + ctx->signed_cert_timestamps_enabled = 1; +} + +void SSL_CTX_i_promise_to_verify_certs_after_the_handshake(SSL_CTX *ctx) { + ctx->i_promise_to_verify_certs_after_the_handshake = 1; +} + +void SSL_enable_signed_cert_timestamps(SSL *ssl) { + ssl->signed_cert_timestamps_enabled = 1; +} + +void SSL_CTX_enable_ocsp_stapling(SSL_CTX *ctx) { + ctx->ocsp_stapling_enabled = 1; +} + +void SSL_enable_ocsp_stapling(SSL *ssl) { + ssl->ocsp_stapling_enabled = 1; +} + +void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, const uint8_t **out, + size_t *out_len) { + SSL_SESSION *session = SSL_get_session(ssl); + + *out_len = 0; + *out = NULL; + if (ssl->server || !session || !session->tlsext_signed_cert_timestamp_list) { + return; + } + + *out = session->tlsext_signed_cert_timestamp_list; + *out_len = session->tlsext_signed_cert_timestamp_list_length; +} + +void SSL_get0_ocsp_response(const SSL *ssl, const uint8_t **out, + size_t *out_len) { + SSL_SESSION *session = SSL_get_session(ssl); + + *out_len = 0; + *out = NULL; + if (ssl->server || !session || !session->ocsp_response) { + return; + } + *out = session->ocsp_response; + *out_len = session->ocsp_response_length; +} + +int SSL_set_tlsext_host_name(SSL *ssl, const char *name) { + OPENSSL_free(ssl->tlsext_hostname); + ssl->tlsext_hostname = NULL; + + if (name == NULL) { + return 1; + } + + size_t len = strlen(name); + if (len == 0 || len > TLSEXT_MAXLEN_host_name) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME); + return 0; + } + ssl->tlsext_hostname = BUF_strdup(name); + if (ssl->tlsext_hostname == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + return 1; +} + +int SSL_CTX_set_tlsext_servername_callback( + SSL_CTX *ctx, int (*callback)(SSL *ssl, int *out_alert, void *arg)) { + ctx->tlsext_servername_callback = callback; + return 1; +} + +int SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg) { + ctx->tlsext_servername_arg = arg; + return 1; +} + +int SSL_select_next_proto(uint8_t **out, uint8_t *out_len, + const uint8_t *server, unsigned server_len, + const uint8_t *client, unsigned client_len) { + unsigned int i, j; + const uint8_t *result; + int status = OPENSSL_NPN_UNSUPPORTED; + + /* For each protocol in server preference order, see if we support it. */ + for (i = 0; i < server_len;) { + for (j = 0; j < client_len;) { + if (server[i] == client[j] && + OPENSSL_memcmp(&server[i + 1], &client[j + 1], server[i]) == 0) { + /* We found a match */ + result = &server[i]; + status = OPENSSL_NPN_NEGOTIATED; + goto found; + } + j += client[j]; + j++; + } + i += server[i]; + i++; + } + + /* There's no overlap between our protocols and the server's list. */ + result = client; + status = OPENSSL_NPN_NO_OVERLAP; + +found: + *out = (uint8_t *)result + 1; + *out_len = result[0]; + return status; +} + +void SSL_get0_next_proto_negotiated(const SSL *ssl, const uint8_t **out_data, + unsigned *out_len) { + *out_data = ssl->s3->next_proto_negotiated; + if (*out_data == NULL) { + *out_len = 0; + } else { + *out_len = ssl->s3->next_proto_negotiated_len; + } +} + +void SSL_CTX_set_next_protos_advertised_cb( + SSL_CTX *ctx, + int (*cb)(SSL *ssl, const uint8_t **out, unsigned *out_len, void *arg), + void *arg) { + ctx->next_protos_advertised_cb = cb; + ctx->next_protos_advertised_cb_arg = arg; +} + +void SSL_CTX_set_next_proto_select_cb( + SSL_CTX *ctx, int (*cb)(SSL *ssl, uint8_t **out, uint8_t *out_len, + const uint8_t *in, unsigned in_len, void *arg), + void *arg) { + ctx->next_proto_select_cb = cb; + ctx->next_proto_select_cb_arg = arg; +} + +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const uint8_t *protos, + unsigned protos_len) { + OPENSSL_free(ctx->alpn_client_proto_list); + ctx->alpn_client_proto_list = BUF_memdup(protos, protos_len); + if (!ctx->alpn_client_proto_list) { + return 1; + } + ctx->alpn_client_proto_list_len = protos_len; + + return 0; +} + +int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, unsigned protos_len) { + OPENSSL_free(ssl->alpn_client_proto_list); + ssl->alpn_client_proto_list = BUF_memdup(protos, protos_len); + if (!ssl->alpn_client_proto_list) { + return 1; + } + ssl->alpn_client_proto_list_len = protos_len; + + return 0; +} + +void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, + int (*cb)(SSL *ssl, const uint8_t **out, + uint8_t *out_len, const uint8_t *in, + unsigned in_len, void *arg), + void *arg) { + ctx->alpn_select_cb = cb; + ctx->alpn_select_cb_arg = arg; +} + +void SSL_get0_alpn_selected(const SSL *ssl, const uint8_t **out_data, + unsigned *out_len) { + *out_data = NULL; + if (ssl->s3) { + *out_data = ssl->s3->alpn_selected; + } + if (*out_data == NULL) { + *out_len = 0; + } else { + *out_len = ssl->s3->alpn_selected_len; + } +} + + +void SSL_CTX_set_tls_channel_id_enabled(SSL_CTX *ctx, int enabled) { + ctx->tlsext_channel_id_enabled = !!enabled; +} + +int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx) { + SSL_CTX_set_tls_channel_id_enabled(ctx, 1); + return 1; +} + +void SSL_set_tls_channel_id_enabled(SSL *ssl, int enabled) { + ssl->tlsext_channel_id_enabled = !!enabled; +} + +int SSL_enable_tls_channel_id(SSL *ssl) { + SSL_set_tls_channel_id_enabled(ssl, 1); + return 1; +} + +static int is_p256_key(EVP_PKEY *private_key) { + const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(private_key); + return ec_key != NULL && + EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) == + NID_X9_62_prime256v1; +} + +int SSL_CTX_set1_tls_channel_id(SSL_CTX *ctx, EVP_PKEY *private_key) { + if (!is_p256_key(private_key)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256); + return 0; + } + + EVP_PKEY_free(ctx->tlsext_channel_id_private); + EVP_PKEY_up_ref(private_key); + ctx->tlsext_channel_id_private = private_key; + ctx->tlsext_channel_id_enabled = 1; + + return 1; +} + +int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) { + if (!is_p256_key(private_key)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256); + return 0; + } + + EVP_PKEY_free(ssl->tlsext_channel_id_private); + EVP_PKEY_up_ref(private_key); + ssl->tlsext_channel_id_private = private_key; + ssl->tlsext_channel_id_enabled = 1; + + return 1; +} + +size_t SSL_get_tls_channel_id(SSL *ssl, uint8_t *out, size_t max_out) { + if (!ssl->s3->tlsext_channel_id_valid) { + return 0; + } + OPENSSL_memcpy(out, ssl->s3->tlsext_channel_id, + (max_out < 64) ? max_out : 64); + return 64; +} + +size_t SSL_get0_certificate_types(SSL *ssl, const uint8_t **out_types) { + if (ssl->server || ssl->s3->hs == NULL) { + *out_types = NULL; + return 0; + } + *out_types = ssl->s3->hs->certificate_types; + return ssl->s3->hs->num_certificate_types; +} + +void ssl_update_cache(SSL_HANDSHAKE *hs, int mode) { + SSL *const ssl = hs->ssl; + SSL_CTX *ctx = ssl->session_ctx; + /* Never cache sessions with empty session IDs. */ + if (ssl->s3->established_session->session_id_length == 0 || + (ctx->session_cache_mode & mode) != mode) { + return; + } + + /* Clients never use the internal session cache. */ + int use_internal_cache = ssl->server && !(ctx->session_cache_mode & + SSL_SESS_CACHE_NO_INTERNAL_STORE); + + /* A client may see new sessions on abbreviated handshakes if the server + * decides to renew the ticket. Once the handshake is completed, it should be + * inserted into the cache. */ + if (ssl->s3->established_session != ssl->session || + (!ssl->server && hs->ticket_expected)) { + if (use_internal_cache) { + SSL_CTX_add_session(ctx, ssl->s3->established_session); + } + if (ctx->new_session_cb != NULL) { + SSL_SESSION_up_ref(ssl->s3->established_session); + if (!ctx->new_session_cb(ssl, ssl->s3->established_session)) { + /* |new_session_cb|'s return value signals whether it took ownership. */ + SSL_SESSION_free(ssl->s3->established_session); + } + } + } + + if (use_internal_cache && + !(ctx->session_cache_mode & SSL_SESS_CACHE_NO_AUTO_CLEAR)) { + /* Automatically flush the internal session cache every 255 connections. */ + int flush_cache = 0; + CRYPTO_MUTEX_lock_write(&ctx->lock); + ctx->handshakes_since_cache_flush++; + if (ctx->handshakes_since_cache_flush >= 255) { + flush_cache = 1; + ctx->handshakes_since_cache_flush = 0; + } + CRYPTO_MUTEX_unlock_write(&ctx->lock); + + if (flush_cache) { + struct OPENSSL_timeval now; + ssl_get_current_time(ssl, &now); + SSL_CTX_flush_sessions(ctx, now.tv_sec); + } + } +} + +static const char *ssl_get_version(int version) { + switch (version) { + /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */ + case TLS1_3_DRAFT_VERSION: + return "TLSv1.3"; + + case TLS1_2_VERSION: + return "TLSv1.2"; + + case TLS1_1_VERSION: + return "TLSv1.1"; + + case TLS1_VERSION: + return "TLSv1"; + + case SSL3_VERSION: + return "SSLv3"; + + case DTLS1_VERSION: + return "DTLSv1"; + + case DTLS1_2_VERSION: + return "DTLSv1.2"; + + default: + return "unknown"; + } +} + +const char *SSL_get_version(const SSL *ssl) { + return ssl_get_version(ssl->version); +} + +const char *SSL_SESSION_get_version(const SSL_SESSION *session) { + return ssl_get_version(session->ssl_version); +} + +EVP_PKEY *SSL_get_privatekey(const SSL *ssl) { + if (ssl->cert != NULL) { + return ssl->cert->privatekey; + } + + return NULL; +} + +EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx) { + if (ctx->cert != NULL) { + return ctx->cert->privatekey; + } + + return NULL; +} + +const SSL_CIPHER *SSL_get_current_cipher(const SSL *ssl) { + if (ssl->s3->aead_write_ctx == NULL) { + return NULL; + } + return ssl->s3->aead_write_ctx->cipher; +} + +int SSL_session_reused(const SSL *ssl) { + return ssl->s3->session_reused; +} + +const COMP_METHOD *SSL_get_current_compression(SSL *ssl) { return NULL; } + +const COMP_METHOD *SSL_get_current_expansion(SSL *ssl) { return NULL; } + +int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; } + +void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) { + ctx->quiet_shutdown = (mode != 0); +} + +int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx) { + return ctx->quiet_shutdown; +} + +void SSL_set_quiet_shutdown(SSL *ssl, int mode) { + ssl->quiet_shutdown = (mode != 0); +} + +int SSL_get_quiet_shutdown(const SSL *ssl) { return ssl->quiet_shutdown; } + +void SSL_set_shutdown(SSL *ssl, int mode) { + /* It is an error to clear any bits that have already been set. (We can't try + * to get a second close_notify or send two.) */ + assert((SSL_get_shutdown(ssl) & mode) == SSL_get_shutdown(ssl)); + + if (mode & SSL_RECEIVED_SHUTDOWN && + ssl->s3->recv_shutdown == ssl_shutdown_none) { + ssl->s3->recv_shutdown = ssl_shutdown_close_notify; + } + + if (mode & SSL_SENT_SHUTDOWN && + ssl->s3->send_shutdown == ssl_shutdown_none) { + ssl->s3->send_shutdown = ssl_shutdown_close_notify; + } +} + +int SSL_get_shutdown(const SSL *ssl) { + int ret = 0; + if (ssl->s3->recv_shutdown != ssl_shutdown_none) { + /* Historically, OpenSSL set |SSL_RECEIVED_SHUTDOWN| on both close_notify + * and fatal alert. */ + ret |= SSL_RECEIVED_SHUTDOWN; + } + if (ssl->s3->send_shutdown == ssl_shutdown_close_notify) { + /* Historically, OpenSSL set |SSL_SENT_SHUTDOWN| on only close_notify. */ + ret |= SSL_SENT_SHUTDOWN; + } + return ret; +} + +int SSL_version(const SSL *ssl) { + /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */ + if (ssl->version == TLS1_3_DRAFT_VERSION) { + return TLS1_3_VERSION; + } + + return ssl->version; +} + +SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) { return ssl->ctx; } + +SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) { + if (ssl->ctx == ctx) { + return ssl->ctx; + } + + /* One cannot change the X.509 callbacks during a connection. */ + if (ssl->ctx->x509_method != ctx->x509_method) { + assert(0); + return NULL; + } + + if (ctx == NULL) { + ctx = ssl->session_ctx; + } + + ssl_cert_free(ssl->cert); + ssl->cert = ssl_cert_dup(ctx->cert); + + SSL_CTX_up_ref(ctx); + SSL_CTX_free(ssl->ctx); + ssl->ctx = ctx; + + return ssl->ctx; +} + +void SSL_set_info_callback(SSL *ssl, + void (*cb)(const SSL *ssl, int type, int value)) { + ssl->info_callback = cb; +} + +void (*SSL_get_info_callback(const SSL *ssl))(const SSL *ssl, int type, + int value) { + return ssl->info_callback; +} + +int SSL_state(const SSL *ssl) { + return SSL_in_init(ssl) ? SSL_ST_INIT : SSL_ST_OK; +} + +void SSL_set_state(SSL *ssl, int state) { } + +char *SSL_get_shared_ciphers(const SSL *ssl, char *buf, int len) { + if (len <= 0) { + return NULL; + } + buf[0] = '\0'; + return buf; +} + +int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) { + int index; + if (!CRYPTO_get_ex_new_index(&g_ex_data_class_ssl, &index, argl, argp, + dup_func, free_func)) { + return -1; + } + return index; +} + +int SSL_set_ex_data(SSL *ssl, int idx, void *arg) { + return CRYPTO_set_ex_data(&ssl->ex_data, idx, arg); +} + +void *SSL_get_ex_data(const SSL *ssl, int idx) { + return CRYPTO_get_ex_data(&ssl->ex_data, idx); +} + +int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused, + CRYPTO_EX_dup *dup_func, + CRYPTO_EX_free *free_func) { + int index; + if (!CRYPTO_get_ex_new_index(&g_ex_data_class_ssl_ctx, &index, argl, argp, + dup_func, free_func)) { + return -1; + } + return index; +} + +int SSL_CTX_set_ex_data(SSL_CTX *ctx, int idx, void *arg) { + return CRYPTO_set_ex_data(&ctx->ex_data, idx, arg); +} + +void *SSL_CTX_get_ex_data(const SSL_CTX *ctx, int idx) { + return CRYPTO_get_ex_data(&ctx->ex_data, idx); +} + +int SSL_want(const SSL *ssl) { return ssl->rwstate; } + +void SSL_CTX_set_tmp_rsa_callback(SSL_CTX *ctx, + RSA *(*cb)(SSL *ssl, int is_export, + int keylength)) { +} + +void SSL_set_tmp_rsa_callback(SSL *ssl, RSA *(*cb)(SSL *ssl, int is_export, + int keylength)) { +} + +void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, + DH *(*callback)(SSL *ssl, int is_export, + int keylength)) { + ctx->cert->dh_tmp_cb = callback; +} + +void SSL_set_tmp_dh_callback(SSL *ssl, DH *(*callback)(SSL *ssl, int is_export, + int keylength)) { + ssl->cert->dh_tmp_cb = callback; +} + +int SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *identity_hint) { + if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + return 0; + } + + OPENSSL_free(ctx->psk_identity_hint); + + if (identity_hint != NULL) { + ctx->psk_identity_hint = BUF_strdup(identity_hint); + if (ctx->psk_identity_hint == NULL) { + return 0; + } + } else { + ctx->psk_identity_hint = NULL; + } + + return 1; +} + +int SSL_use_psk_identity_hint(SSL *ssl, const char *identity_hint) { + if (ssl == NULL) { + return 0; + } + + if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + return 0; + } + + /* Clear currently configured hint, if any. */ + OPENSSL_free(ssl->psk_identity_hint); + ssl->psk_identity_hint = NULL; + + /* Treat the empty hint as not supplying one. Plain PSK makes it possible to + * send either no hint (omit ServerKeyExchange) or an empty hint, while + * ECDHE_PSK can only spell empty hint. Having different capabilities is odd, + * so we interpret empty and missing as identical. */ + if (identity_hint != NULL && identity_hint[0] != '\0') { + ssl->psk_identity_hint = BUF_strdup(identity_hint); + if (ssl->psk_identity_hint == NULL) { + return 0; + } + } + + return 1; +} + +const char *SSL_get_psk_identity_hint(const SSL *ssl) { + if (ssl == NULL) { + return NULL; + } + return ssl->psk_identity_hint; +} + +const char *SSL_get_psk_identity(const SSL *ssl) { + if (ssl == NULL) { + return NULL; + } + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL) { + return NULL; + } + return session->psk_identity; +} + +void SSL_set_psk_client_callback( + SSL *ssl, unsigned (*cb)(SSL *ssl, const char *hint, char *identity, + unsigned max_identity_len, uint8_t *psk, + unsigned max_psk_len)) { + ssl->psk_client_callback = cb; +} + +void SSL_CTX_set_psk_client_callback( + SSL_CTX *ctx, unsigned (*cb)(SSL *ssl, const char *hint, char *identity, + unsigned max_identity_len, uint8_t *psk, + unsigned max_psk_len)) { + ctx->psk_client_callback = cb; +} + +void SSL_set_psk_server_callback( + SSL *ssl, unsigned (*cb)(SSL *ssl, const char *identity, uint8_t *psk, + unsigned max_psk_len)) { + ssl->psk_server_callback = cb; +} + +void SSL_CTX_set_psk_server_callback( + SSL_CTX *ctx, unsigned (*cb)(SSL *ssl, const char *identity, + uint8_t *psk, unsigned max_psk_len)) { + ctx->psk_server_callback = cb; +} + +void SSL_CTX_set_msg_callback(SSL_CTX *ctx, + void (*cb)(int write_p, int version, + int content_type, const void *buf, + size_t len, SSL *ssl, void *arg)) { + ctx->msg_callback = cb; +} + +void SSL_CTX_set_msg_callback_arg(SSL_CTX *ctx, void *arg) { + ctx->msg_callback_arg = arg; +} + +void SSL_set_msg_callback(SSL *ssl, + void (*cb)(int write_p, int version, int content_type, + const void *buf, size_t len, SSL *ssl, + void *arg)) { + ssl->msg_callback = cb; +} + +void SSL_set_msg_callback_arg(SSL *ssl, void *arg) { + ssl->msg_callback_arg = arg; +} + +void SSL_CTX_set_keylog_callback(SSL_CTX *ctx, + void (*cb)(const SSL *ssl, const char *line)) { + ctx->keylog_callback = cb; +} + +void (*SSL_CTX_get_keylog_callback(const SSL_CTX *ctx))(const SSL *ssl, + const char *line) { + return ctx->keylog_callback; +} + +void SSL_CTX_set_current_time_cb(SSL_CTX *ctx, + void (*cb)(const SSL *ssl, + struct timeval *out_clock)) { + ctx->current_time_cb = cb; +} + +static int cbb_add_hex(CBB *cbb, const uint8_t *in, size_t in_len) { + static const char hextable[] = "0123456789abcdef"; + uint8_t *out; + + if (!CBB_add_space(cbb, &out, in_len * 2)) { + return 0; + } + + for (size_t i = 0; i < in_len; i++) { + *(out++) = (uint8_t)hextable[in[i] >> 4]; + *(out++) = (uint8_t)hextable[in[i] & 0xf]; + } + + return 1; +} + +int ssl_log_secret(const SSL *ssl, const char *label, const uint8_t *secret, + size_t secret_len) { + if (ssl->ctx->keylog_callback == NULL) { + return 1; + } + + CBB cbb; + uint8_t *out; + size_t out_len; + if (!CBB_init(&cbb, strlen(label) + 1 + SSL3_RANDOM_SIZE * 2 + 1 + + secret_len * 2 + 1) || + !CBB_add_bytes(&cbb, (const uint8_t *)label, strlen(label)) || + !CBB_add_bytes(&cbb, (const uint8_t *)" ", 1) || + !cbb_add_hex(&cbb, ssl->s3->client_random, SSL3_RANDOM_SIZE) || + !CBB_add_bytes(&cbb, (const uint8_t *)" ", 1) || + !cbb_add_hex(&cbb, secret, secret_len) || + !CBB_add_u8(&cbb, 0 /* NUL */) || + !CBB_finish(&cbb, &out, &out_len)) { + CBB_cleanup(&cbb); + return 0; + } + + ssl->ctx->keylog_callback(ssl, (const char *)out); + OPENSSL_free(out); + return 1; +} + +int SSL_is_init_finished(const SSL *ssl) { + return !SSL_in_init(ssl); +} + +int SSL_in_init(const SSL *ssl) { + SSL_HANDSHAKE *hs = ssl->s3->hs; + return hs != NULL && hs->state != SSL_ST_OK; +} + +int SSL_in_false_start(const SSL *ssl) { + if (ssl->s3->hs == NULL) { + return 0; + } + return ssl->s3->hs->in_false_start; +} + +int SSL_cutthrough_complete(const SSL *ssl) { + return SSL_in_false_start(ssl); +} + +void SSL_get_structure_sizes(size_t *ssl_size, size_t *ssl_ctx_size, + size_t *ssl_session_size) { + *ssl_size = sizeof(SSL); + *ssl_ctx_size = sizeof(SSL_CTX); + *ssl_session_size = sizeof(SSL_SESSION); +} + +int ssl3_can_false_start(const SSL *ssl) { + const SSL_CIPHER *const cipher = SSL_get_current_cipher(ssl); + + /* False Start only for TLS 1.2 with an ECDHE+AEAD cipher and ALPN or NPN. */ + return !SSL_is_dtls(ssl) && + SSL_version(ssl) == TLS1_2_VERSION && + (ssl->s3->alpn_selected != NULL || + ssl->s3->next_proto_negotiated != NULL) && + cipher != NULL && + cipher->algorithm_mkey == SSL_kECDHE && + cipher->algorithm_mac == SSL_AEAD; +} + +const struct { + uint16_t version; + uint32_t flag; +} kVersions[] = { + {SSL3_VERSION, SSL_OP_NO_SSLv3}, + {TLS1_VERSION, SSL_OP_NO_TLSv1}, + {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1}, + {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2}, + {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3}, +}; + +static const size_t kVersionsLen = OPENSSL_ARRAY_SIZE(kVersions); + +int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version, + uint16_t *out_max_version) { + /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but + * DTLS 1.0 should be mapped to TLS 1.1. */ + uint32_t options = ssl->options; + if (SSL_is_dtls(ssl)) { + options &= ~SSL_OP_NO_TLSv1_1; + if (options & SSL_OP_NO_DTLSv1) { + options |= SSL_OP_NO_TLSv1_1; + } + } + + uint16_t min_version = ssl->min_version; + uint16_t max_version = ssl->max_version; + + /* Bound the range to only those implemented in this protocol. */ + if (min_version < ssl->method->min_version) { + min_version = ssl->method->min_version; + } + if (max_version > ssl->method->max_version) { + max_version = ssl->method->max_version; + } + + /* OpenSSL's API for controlling versions entails blacklisting individual + * protocols. This has two problems. First, on the client, the protocol can + * only express a contiguous range of versions. Second, a library consumer + * trying to set a maximum version cannot disable protocol versions that get + * added in a future version of the library. + * + * To account for both of these, OpenSSL interprets the client-side bitmask + * as a min/max range by picking the lowest contiguous non-empty range of + * enabled protocols. Note that this means it is impossible to set a maximum + * version of the higest supported TLS version in a future-proof way. */ + int any_enabled = 0; + for (size_t i = 0; i < kVersionsLen; i++) { + /* Only look at the versions already enabled. */ + if (min_version > kVersions[i].version) { + continue; + } + if (max_version < kVersions[i].version) { + break; + } + + if (!(options & kVersions[i].flag)) { + /* The minimum version is the first enabled version. */ + if (!any_enabled) { + any_enabled = 1; + min_version = kVersions[i].version; + } + continue; + } + + /* If there is a disabled version after the first enabled one, all versions + * after it are implicitly disabled. */ + if (any_enabled) { + max_version = kVersions[i-1].version; + break; + } + } + + if (!any_enabled) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION); + return 0; + } + + *out_min_version = min_version; + *out_max_version = max_version; + return 1; +} + +uint16_t ssl3_protocol_version(const SSL *ssl) { + assert(ssl->s3->have_version); + uint16_t version; + if (!ssl->method->version_from_wire(&version, ssl->version)) { + /* TODO(davidben): Use the internal version representation for ssl->version + * and map to the public API representation at API boundaries. */ + assert(0); + return 0; + } + + return version; +} + +int SSL_is_server(const SSL *ssl) { return ssl->server; } + +int SSL_is_dtls(const SSL *ssl) { return ssl->method->is_dtls; } + +void SSL_CTX_set_select_certificate_cb(SSL_CTX *ctx, + int (*cb)(const SSL_CLIENT_HELLO *)) { + ctx->select_certificate_cb = cb; +} + +void SSL_CTX_set_dos_protection_cb(SSL_CTX *ctx, + int (*cb)(const SSL_CLIENT_HELLO *)) { + ctx->dos_protection_cb = cb; +} + +void SSL_set_renegotiate_mode(SSL *ssl, enum ssl_renegotiate_mode_t mode) { + ssl->renegotiate_mode = mode; +} + +int SSL_get_ivs(const SSL *ssl, const uint8_t **out_read_iv, + const uint8_t **out_write_iv, size_t *out_iv_len) { + if (ssl->s3->aead_read_ctx == NULL || ssl->s3->aead_write_ctx == NULL) { + return 0; + } + + size_t write_iv_len; + if (!EVP_AEAD_CTX_get_iv(&ssl->s3->aead_read_ctx->ctx, out_read_iv, + out_iv_len) || + !EVP_AEAD_CTX_get_iv(&ssl->s3->aead_write_ctx->ctx, out_write_iv, + &write_iv_len) || + *out_iv_len != write_iv_len) { + return 0; + } + + return 1; +} + +static uint64_t be_to_u64(const uint8_t in[8]) { + return (((uint64_t)in[0]) << 56) | (((uint64_t)in[1]) << 48) | + (((uint64_t)in[2]) << 40) | (((uint64_t)in[3]) << 32) | + (((uint64_t)in[4]) << 24) | (((uint64_t)in[5]) << 16) | + (((uint64_t)in[6]) << 8) | ((uint64_t)in[7]); +} + +uint64_t SSL_get_read_sequence(const SSL *ssl) { + /* TODO(davidben): Internally represent sequence numbers as uint64_t. */ + if (SSL_is_dtls(ssl)) { + /* max_seq_num already includes the epoch. */ + assert(ssl->d1->r_epoch == (ssl->d1->bitmap.max_seq_num >> 48)); + return ssl->d1->bitmap.max_seq_num; + } + return be_to_u64(ssl->s3->read_sequence); +} + +uint64_t SSL_get_write_sequence(const SSL *ssl) { + uint64_t ret = be_to_u64(ssl->s3->write_sequence); + if (SSL_is_dtls(ssl)) { + assert((ret >> 48) == 0); + ret |= ((uint64_t)ssl->d1->w_epoch) << 48; + } + return ret; +} + +uint16_t SSL_get_peer_signature_algorithm(const SSL *ssl) { + /* TODO(davidben): This checks the wrong session if there is a renegotiation + * in progress. */ + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL) { + return 0; + } + + return session->peer_signature_algorithm; +} + +size_t SSL_get_client_random(const SSL *ssl, uint8_t *out, size_t max_out) { + if (max_out == 0) { + return sizeof(ssl->s3->client_random); + } + if (max_out > sizeof(ssl->s3->client_random)) { + max_out = sizeof(ssl->s3->client_random); + } + OPENSSL_memcpy(out, ssl->s3->client_random, max_out); + return max_out; +} + +size_t SSL_get_server_random(const SSL *ssl, uint8_t *out, size_t max_out) { + if (max_out == 0) { + return sizeof(ssl->s3->server_random); + } + if (max_out > sizeof(ssl->s3->server_random)) { + max_out = sizeof(ssl->s3->server_random); + } + OPENSSL_memcpy(out, ssl->s3->server_random, max_out); + return max_out; +} + +const SSL_CIPHER *SSL_get_pending_cipher(const SSL *ssl) { + SSL_HANDSHAKE *hs = ssl->s3->hs; + if (hs == NULL) { + return NULL; + } + return hs->new_cipher; +} + +void SSL_set_retain_only_sha256_of_client_certs(SSL *ssl, int enabled) { + ssl->retain_only_sha256_of_client_certs = !!enabled; +} + +void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, int enabled) { + ctx->retain_only_sha256_of_client_certs = !!enabled; +} + +void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled) { + ctx->grease_enabled = !!enabled; +} + +int32_t SSL_get_ticket_age_skew(const SSL *ssl) { + return ssl->s3->ticket_age_skew; +} + +int SSL_clear(SSL *ssl) { + /* In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously + * established session to be offered the next time around. wpa_supplicant + * depends on this behavior, so emulate it. */ + SSL_SESSION *session = NULL; + if (!ssl->server && ssl->s3->established_session != NULL) { + session = ssl->s3->established_session; + SSL_SESSION_up_ref(session); + } + + /* TODO(davidben): Some state on |ssl| is reset both in |SSL_new| and + * |SSL_clear| because it is per-connection state rather than configuration + * state. Per-connection state should be on |ssl->s3| and |ssl->d1| so it is + * naturally reset at the right points between |SSL_new|, |SSL_clear|, and + * |ssl3_new|. */ + + ssl->rwstate = SSL_NOTHING; + + BUF_MEM_free(ssl->init_buf); + ssl->init_buf = NULL; + ssl->init_msg = NULL; + ssl->init_num = 0; + + /* The ssl->d1->mtu is simultaneously configuration (preserved across + * clear) and connection-specific state (gets reset). + * + * TODO(davidben): Avoid this. */ + unsigned mtu = 0; + if (ssl->d1 != NULL) { + mtu = ssl->d1->mtu; + } + + ssl->method->ssl_free(ssl); + if (!ssl->method->ssl_new(ssl)) { + SSL_SESSION_free(session); + return 0; + } + + if (SSL_is_dtls(ssl) && (SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) { + ssl->d1->mtu = mtu; + } + + if (session != NULL) { + SSL_set_session(ssl, session); + SSL_SESSION_free(session); + } + + return 1; +} + +void ssl_do_info_callback(const SSL *ssl, int type, int value) { + void (*cb)(const SSL *ssl, int type, int value) = NULL; + if (ssl->info_callback != NULL) { + cb = ssl->info_callback; + } else if (ssl->ctx->info_callback != NULL) { + cb = ssl->ctx->info_callback; + } + + if (cb != NULL) { + cb(ssl, type, value); + } +} + +void ssl_do_msg_callback(SSL *ssl, int is_write, int content_type, + const void *buf, size_t len) { + if (ssl->msg_callback == NULL) { + return; + } + + /* |version| is zero when calling for |SSL3_RT_HEADER| and |SSL2_VERSION| for + * a V2ClientHello. */ + int version; + switch (content_type) { + case 0: + /* V2ClientHello */ + version = SSL2_VERSION; + break; + case SSL3_RT_HEADER: + version = 0; + break; + default: + version = SSL_version(ssl); + } + + ssl->msg_callback(is_write, version, content_type, buf, len, ssl, + ssl->msg_callback_arg); +} + +int SSL_CTX_sess_connect(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_connect_good(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_connect_renegotiate(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_accept(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_accept_renegotiate(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_accept_good(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_hits(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_cb_hits(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_misses(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_timeouts(const SSL_CTX *ctx) { return 0; } +int SSL_CTX_sess_cache_full(const SSL_CTX *ctx) { return 0; } + +int SSL_num_renegotiations(const SSL *ssl) { + return SSL_total_renegotiations(ssl); +} + +int SSL_CTX_need_tmp_RSA(const SSL_CTX *ctx) { return 0; } +int SSL_need_tmp_RSA(const SSL *ssl) { return 0; } +int SSL_CTX_set_tmp_rsa(SSL_CTX *ctx, const RSA *rsa) { return 1; } +int SSL_set_tmp_rsa(SSL *ssl, const RSA *rsa) { return 1; } +void ERR_load_SSL_strings(void) {} +void SSL_load_error_strings(void) {} +int SSL_cache_hit(SSL *ssl) { return SSL_session_reused(ssl); } + +int SSL_CTX_set_tmp_ecdh(SSL_CTX *ctx, const EC_KEY *ec_key) { + if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); + return SSL_CTX_set1_curves(ctx, &nid, 1); +} + +int SSL_set_tmp_ecdh(SSL *ssl, const EC_KEY *ec_key) { + if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); + return SSL_set1_curves(ssl, &nid, 1); +} + +void ssl_get_current_time(const SSL *ssl, struct OPENSSL_timeval *out_clock) { + if (ssl->ctx->current_time_cb != NULL) { + /* TODO(davidben): Update current_time_cb to use OPENSSL_timeval. See + * https://crbug.com/boringssl/155. */ + struct timeval clock; + ssl->ctx->current_time_cb(ssl, &clock); + if (clock.tv_sec < 0) { + assert(0); + out_clock->tv_sec = 0; + out_clock->tv_usec = 0; + } else { + out_clock->tv_sec = (uint64_t)clock.tv_sec; + out_clock->tv_usec = (uint32_t)clock.tv_usec; + } + return; + } + +#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) + out_clock->tv_sec = 1234; + out_clock->tv_usec = 1234; +#elif defined(OPENSSL_WINDOWS) + struct _timeb time; + _ftime(&time); + if (time.time < 0) { + assert(0); + out_clock->tv_sec = 0; + out_clock->tv_usec = 0; + } else { + out_clock->tv_sec = time.time; + out_clock->tv_usec = time.millitm * 1000; + } +#else + struct timeval clock; + gettimeofday(&clock, NULL); + if (clock.tv_sec < 0) { + assert(0); + out_clock->tv_sec = 0; + out_clock->tv_usec = 0; + } else { + out_clock->tv_sec = (uint64_t)clock.tv_sec; + out_clock->tv_usec = (uint32_t)clock.tv_usec; + } +#endif +} + +int SSL_CTX_set_min_version(SSL_CTX *ctx, uint16_t version) { + return SSL_CTX_set_min_proto_version(ctx, version); +} + +int SSL_CTX_set_max_version(SSL_CTX *ctx, uint16_t version) { + return SSL_CTX_set_max_proto_version(ctx, version); +} + +int SSL_set_min_version(SSL *ssl, uint16_t version) { + return SSL_set_min_proto_version(ssl, version); +} + +int SSL_set_max_version(SSL *ssl, uint16_t version) { + return SSL_set_max_proto_version(ssl, version); +} + +void SSL_CTX_set_ticket_aead_method(SSL_CTX *ctx, + const SSL_TICKET_AEAD_METHOD *aead_method) { + ctx->ticket_aead_method = aead_method; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_privkey.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_privkey.c new file mode 100644 index 000000000..e988827f5 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_privkey.c @@ -0,0 +1,678 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] */ + +#include <openssl/ssl.h> + +#include <limits.h> + +#include <openssl/ec.h> +#include <openssl/ec_key.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/type_check.h> + +#include "internal.h" + + +int ssl_is_key_type_supported(int key_type) { + return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC; +} + +static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey) { + if (!ssl_is_key_type_supported(pkey->type)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + return 0; + } + + if (cert->chain != NULL && + sk_CRYPTO_BUFFER_value(cert->chain, 0) != NULL && + /* Sanity-check that the private key and the certificate match. */ + !ssl_cert_check_private_key(cert, pkey)) { + return 0; + } + + EVP_PKEY_free(cert->privatekey); + EVP_PKEY_up_ref(pkey); + cert->privatekey = pkey; + + return 1; +} + +int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) { + EVP_PKEY *pkey; + int ret; + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB); + return 0; + } + + RSA_up_ref(rsa); + EVP_PKEY_assign_RSA(pkey, rsa); + + ret = ssl_set_pkey(ssl->cert, pkey); + EVP_PKEY_free(pkey); + + return ret; +} + +int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) { + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return ssl_set_pkey(ssl->cert, pkey); +} + +int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *der, + size_t der_len) { + if (der_len > LONG_MAX) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + + const uint8_t *p = der; + EVP_PKEY *pkey = d2i_PrivateKey(type, NULL, &p, (long)der_len); + if (pkey == NULL || p != der + der_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); + EVP_PKEY_free(pkey); + return 0; + } + + int ret = SSL_use_PrivateKey(ssl, pkey); + EVP_PKEY_free(pkey); + return ret; +} + +int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa) { + int ret; + EVP_PKEY *pkey; + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_EVP_LIB); + return 0; + } + + RSA_up_ref(rsa); + EVP_PKEY_assign_RSA(pkey, rsa); + + ret = ssl_set_pkey(ctx->cert, pkey); + EVP_PKEY_free(pkey); + return ret; +} + +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const uint8_t *der, + size_t der_len) { + RSA *rsa = RSA_private_key_from_bytes(der, der_len); + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); + return 0; + } + + int ret = SSL_CTX_use_RSAPrivateKey(ctx, rsa); + RSA_free(rsa); + return ret; +} + +int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) { + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return ssl_set_pkey(ctx->cert, pkey); +} + +int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *der, + size_t der_len) { + if (der_len > LONG_MAX) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + + const uint8_t *p = der; + EVP_PKEY *pkey = d2i_PrivateKey(type, NULL, &p, (long)der_len); + if (pkey == NULL || p != der + der_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); + EVP_PKEY_free(pkey); + return 0; + } + + int ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); + return ret; +} + +void SSL_set_private_key_method(SSL *ssl, + const SSL_PRIVATE_KEY_METHOD *key_method) { + ssl->cert->key_method = key_method; +} + +void SSL_CTX_set_private_key_method(SSL_CTX *ctx, + const SSL_PRIVATE_KEY_METHOD *key_method) { + ctx->cert->key_method = key_method; +} + +static int set_signing_algorithm_prefs(CERT *cert, const uint16_t *prefs, + size_t num_prefs) { + OPENSSL_free(cert->sigalgs); + + cert->num_sigalgs = 0; + cert->sigalgs = BUF_memdup(prefs, num_prefs * sizeof(prefs[0])); + if (cert->sigalgs == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + cert->num_sigalgs = num_prefs; + + return 1; +} + +int SSL_CTX_set_signing_algorithm_prefs(SSL_CTX *ctx, const uint16_t *prefs, + size_t num_prefs) { + return set_signing_algorithm_prefs(ctx->cert, prefs, num_prefs); +} + + +int SSL_set_signing_algorithm_prefs(SSL *ssl, const uint16_t *prefs, + size_t num_prefs) { + return set_signing_algorithm_prefs(ssl->cert, prefs, num_prefs); +} + +int SSL_set_private_key_digest_prefs(SSL *ssl, const int *digest_nids, + size_t num_digests) { + OPENSSL_free(ssl->cert->sigalgs); + + OPENSSL_COMPILE_ASSERT(sizeof(int) >= 2 * sizeof(uint16_t), + digest_list_conversion_cannot_overflow); + + ssl->cert->num_sigalgs = 0; + ssl->cert->sigalgs = OPENSSL_malloc(sizeof(uint16_t) * 2 * num_digests); + if (ssl->cert->sigalgs == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + + /* Convert the digest list to a signature algorithms list. + * + * TODO(davidben): Replace this API with one that can express RSA-PSS, etc. */ + for (size_t i = 0; i < num_digests; i++) { + switch (digest_nids[i]) { + case NID_sha1: + ssl->cert->sigalgs[ssl->cert->num_sigalgs] = SSL_SIGN_RSA_PKCS1_SHA1; + ssl->cert->sigalgs[ssl->cert->num_sigalgs + 1] = SSL_SIGN_ECDSA_SHA1; + ssl->cert->num_sigalgs += 2; + break; + case NID_sha256: + ssl->cert->sigalgs[ssl->cert->num_sigalgs] = SSL_SIGN_RSA_PKCS1_SHA256; + ssl->cert->sigalgs[ssl->cert->num_sigalgs + 1] = + SSL_SIGN_ECDSA_SECP256R1_SHA256; + ssl->cert->num_sigalgs += 2; + break; + case NID_sha384: + ssl->cert->sigalgs[ssl->cert->num_sigalgs] = SSL_SIGN_RSA_PKCS1_SHA384; + ssl->cert->sigalgs[ssl->cert->num_sigalgs + 1] = + SSL_SIGN_ECDSA_SECP384R1_SHA384; + ssl->cert->num_sigalgs += 2; + break; + case NID_sha512: + ssl->cert->sigalgs[ssl->cert->num_sigalgs] = SSL_SIGN_RSA_PKCS1_SHA512; + ssl->cert->sigalgs[ssl->cert->num_sigalgs + 1] = + SSL_SIGN_ECDSA_SECP521R1_SHA512; + ssl->cert->num_sigalgs += 2; + break; + } + } + + return 1; +} + +int ssl_has_private_key(const SSL *ssl) { + return ssl->cert->privatekey != NULL || ssl->cert->key_method != NULL; +} + +int ssl_is_ecdsa_key_type(int type) { + switch (type) { + case NID_secp224r1: + case NID_X9_62_prime256v1: + case NID_secp384r1: + case NID_secp521r1: + return 1; + default: + return 0; + } +} + +int ssl_private_key_type(SSL *ssl) { + if (ssl->cert->key_method != NULL) { + return ssl->cert->key_method->type(ssl); + } + switch (EVP_PKEY_id(ssl->cert->privatekey)) { + case EVP_PKEY_RSA: + return NID_rsaEncryption; + case EVP_PKEY_EC: + return EC_GROUP_get_curve_name( + EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(ssl->cert->privatekey))); + default: + return NID_undef; + } +} + +size_t ssl_private_key_max_signature_len(SSL *ssl) { + if (ssl->cert->key_method != NULL) { + return ssl->cert->key_method->max_signature_len(ssl); + } + return EVP_PKEY_size(ssl->cert->privatekey); +} + +/* TODO(davidben): Forbid RSA-PKCS1 in TLS 1.3. For now we allow it because NSS + * has yet to start doing RSA-PSS, so enforcing it would complicate interop + * testing. */ +static int is_rsa_pkcs1(const EVP_MD **out_md, uint16_t sigalg) { + switch (sigalg) { + case SSL_SIGN_RSA_PKCS1_MD5_SHA1: + *out_md = EVP_md5_sha1(); + return 1; + case SSL_SIGN_RSA_PKCS1_SHA1: + *out_md = EVP_sha1(); + return 1; + case SSL_SIGN_RSA_PKCS1_SHA256: + *out_md = EVP_sha256(); + return 1; + case SSL_SIGN_RSA_PKCS1_SHA384: + *out_md = EVP_sha384(); + return 1; + case SSL_SIGN_RSA_PKCS1_SHA512: + *out_md = EVP_sha512(); + return 1; + default: + return 0; + } +} + +static int ssl_sign_rsa_pkcs1(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out, const EVP_MD *md, + const uint8_t *in, size_t in_len) { + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); + *out_len = max_out; + int ret = EVP_DigestSignInit(&ctx, NULL, md, NULL, ssl->cert->privatekey) && + EVP_DigestSignUpdate(&ctx, in, in_len) && + EVP_DigestSignFinal(&ctx, out, out_len); + EVP_MD_CTX_cleanup(&ctx); + return ret; +} + +static int ssl_verify_rsa_pkcs1(SSL *ssl, const uint8_t *signature, + size_t signature_len, const EVP_MD *md, + EVP_PKEY *pkey, const uint8_t *in, + size_t in_len) { + if (pkey->type != EVP_PKEY_RSA) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + return 0; + } + + EVP_MD_CTX md_ctx; + EVP_MD_CTX_init(&md_ctx); + int ret = EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) && + EVP_DigestVerifyUpdate(&md_ctx, in, in_len) && + EVP_DigestVerifyFinal(&md_ctx, signature, signature_len); + EVP_MD_CTX_cleanup(&md_ctx); + return ret; +} + +static int is_ecdsa(int *out_curve, const EVP_MD **out_md, uint16_t sigalg) { + switch (sigalg) { + case SSL_SIGN_ECDSA_SHA1: + *out_curve = NID_undef; + *out_md = EVP_sha1(); + return 1; + case SSL_SIGN_ECDSA_SECP256R1_SHA256: + *out_curve = NID_X9_62_prime256v1; + *out_md = EVP_sha256(); + return 1; + case SSL_SIGN_ECDSA_SECP384R1_SHA384: + *out_curve = NID_secp384r1; + *out_md = EVP_sha384(); + return 1; + case SSL_SIGN_ECDSA_SECP521R1_SHA512: + *out_curve = NID_secp521r1; + *out_md = EVP_sha512(); + return 1; + default: + return 0; + } +} + +static int ssl_sign_ecdsa(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out, int curve, const EVP_MD *md, + const uint8_t *in, size_t in_len) { + EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->cert->privatekey); + if (ec_key == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + return 0; + } + + /* In TLS 1.3, the curve is also specified by the signature algorithm. */ + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION && + (curve == NID_undef || + EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) != curve)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + return 0; + } + + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); + *out_len = max_out; + int ret = EVP_DigestSignInit(&ctx, NULL, md, NULL, ssl->cert->privatekey) && + EVP_DigestSignUpdate(&ctx, in, in_len) && + EVP_DigestSignFinal(&ctx, out, out_len); + EVP_MD_CTX_cleanup(&ctx); + return ret; +} + +static int ssl_verify_ecdsa(SSL *ssl, const uint8_t *signature, + size_t signature_len, int curve, const EVP_MD *md, + EVP_PKEY *pkey, const uint8_t *in, size_t in_len) { + EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); + if (ec_key == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + return 0; + } + + /* In TLS 1.3, the curve is also specified by the signature algorithm. */ + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION && + (curve == NID_undef || + EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) != curve)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + return 0; + } + + EVP_MD_CTX md_ctx; + EVP_MD_CTX_init(&md_ctx); + int ret = EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) && + EVP_DigestVerifyUpdate(&md_ctx, in, in_len) && + EVP_DigestVerifyFinal(&md_ctx, signature, signature_len); + EVP_MD_CTX_cleanup(&md_ctx); + return ret; +} + +static int is_rsa_pss(const EVP_MD **out_md, uint16_t sigalg) { + switch (sigalg) { + case SSL_SIGN_RSA_PSS_SHA256: + *out_md = EVP_sha256(); + return 1; + case SSL_SIGN_RSA_PSS_SHA384: + *out_md = EVP_sha384(); + return 1; + case SSL_SIGN_RSA_PSS_SHA512: + *out_md = EVP_sha512(); + return 1; + default: + return 0; + } +} + +static int ssl_sign_rsa_pss(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out, const EVP_MD *md, + const uint8_t *in, size_t in_len) { + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); + *out_len = max_out; + EVP_PKEY_CTX *pctx; + int ret = + EVP_DigestSignInit(&ctx, &pctx, md, NULL, ssl->cert->privatekey) && + EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) && + EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* salt len = hash len */) && + EVP_DigestSignUpdate(&ctx, in, in_len) && + EVP_DigestSignFinal(&ctx, out, out_len); + EVP_MD_CTX_cleanup(&ctx); + return ret; +} + +static int ssl_verify_rsa_pss(SSL *ssl, const uint8_t *signature, + size_t signature_len, const EVP_MD *md, + EVP_PKEY *pkey, const uint8_t *in, + size_t in_len) { + if (pkey->type != EVP_PKEY_RSA) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + return 0; + } + + EVP_MD_CTX md_ctx; + EVP_MD_CTX_init(&md_ctx); + EVP_PKEY_CTX *pctx; + int ret = + EVP_DigestVerifyInit(&md_ctx, &pctx, md, NULL, pkey) && + EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) && + EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1 /* salt len = hash len */) && + EVP_DigestVerifyUpdate(&md_ctx, in, in_len) && + EVP_DigestVerifyFinal(&md_ctx, signature, signature_len); + EVP_MD_CTX_cleanup(&md_ctx); + return ret; +} + +enum ssl_private_key_result_t ssl_private_key_sign( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint16_t signature_algorithm, const uint8_t *in, size_t in_len) { + if (ssl->cert->key_method != NULL) { + if (ssl->cert->key_method->sign != NULL) { + return ssl->cert->key_method->sign(ssl, out, out_len, max_out, + signature_algorithm, in, in_len); + } + + /* TODO(davidben): Remove support for |sign_digest|-only + * |SSL_PRIVATE_KEY_METHOD|s. */ + const EVP_MD *md; + int curve; + if (!is_rsa_pkcs1(&md, signature_algorithm) && + !is_ecdsa(&curve, &md, signature_algorithm)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY); + return ssl_private_key_failure; + } + + uint8_t hash[EVP_MAX_MD_SIZE]; + unsigned hash_len; + if (!EVP_Digest(in, in_len, hash, &hash_len, md, NULL)) { + return ssl_private_key_failure; + } + + return ssl->cert->key_method->sign_digest(ssl, out, out_len, max_out, md, + hash, hash_len); + } + + const EVP_MD *md; + if (is_rsa_pkcs1(&md, signature_algorithm) && + ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + return ssl_sign_rsa_pkcs1(ssl, out, out_len, max_out, md, in, in_len) + ? ssl_private_key_success + : ssl_private_key_failure; + } + + int curve; + if (is_ecdsa(&curve, &md, signature_algorithm)) { + return ssl_sign_ecdsa(ssl, out, out_len, max_out, curve, md, in, in_len) + ? ssl_private_key_success + : ssl_private_key_failure; + } + + if (is_rsa_pss(&md, signature_algorithm)) { + return ssl_sign_rsa_pss(ssl, out, out_len, max_out, md, in, in_len) + ? ssl_private_key_success + : ssl_private_key_failure; + } + + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + return ssl_private_key_failure; +} + +int ssl_public_key_verify(SSL *ssl, const uint8_t *signature, + size_t signature_len, uint16_t signature_algorithm, + EVP_PKEY *pkey, const uint8_t *in, size_t in_len) { + const EVP_MD *md; + if (is_rsa_pkcs1(&md, signature_algorithm) && + ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + return ssl_verify_rsa_pkcs1(ssl, signature, signature_len, md, pkey, in, + in_len); + } + + int curve; + if (is_ecdsa(&curve, &md, signature_algorithm)) { + return ssl_verify_ecdsa(ssl, signature, signature_len, curve, md, pkey, in, + in_len); + } + + if (is_rsa_pss(&md, signature_algorithm)) { + return ssl_verify_rsa_pss(ssl, signature, signature_len, md, pkey, in, + in_len); + } + + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + return 0; +} + +enum ssl_private_key_result_t ssl_private_key_decrypt( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + const uint8_t *in, size_t in_len) { + if (ssl->cert->key_method != NULL) { + return ssl->cert->key_method->decrypt(ssl, out, out_len, max_out, in, + in_len); + } + + RSA *rsa = EVP_PKEY_get0_RSA(ssl->cert->privatekey); + if (rsa == NULL) { + /* Decrypt operations are only supported for RSA keys. */ + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return ssl_private_key_failure; + } + + /* Decrypt with no padding. PKCS#1 padding will be removed as part + * of the timing-sensitive code by the caller. */ + if (!RSA_decrypt(rsa, out_len, out, max_out, in, in_len, RSA_NO_PADDING)) { + return ssl_private_key_failure; + } + return ssl_private_key_success; +} + +enum ssl_private_key_result_t ssl_private_key_complete(SSL *ssl, uint8_t *out, + size_t *out_len, + size_t max_out) { + /* Only custom keys may be asynchronous. */ + return ssl->cert->key_method->complete(ssl, out, out_len, max_out); +} + +int ssl_private_key_supports_signature_algorithm(SSL *ssl, + uint16_t signature_algorithm) { + const EVP_MD *md; + if (is_rsa_pkcs1(&md, signature_algorithm) && + ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + return ssl_private_key_type(ssl) == NID_rsaEncryption; + } + + int curve; + if (is_ecdsa(&curve, &md, signature_algorithm)) { + int type = ssl_private_key_type(ssl); + if (!ssl_is_ecdsa_key_type(type)) { + return 0; + } + + /* Prior to TLS 1.3, ECDSA curves did not match the signature algorithm. */ + if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + return 1; + } + + return curve != NID_undef && type == curve; + } + + if (is_rsa_pss(&md, signature_algorithm)) { + if (ssl_private_key_type(ssl) != NID_rsaEncryption) { + return 0; + } + + /* Ensure the RSA key is large enough for the hash. RSASSA-PSS requires that + * emLen be at least hLen + sLen + 2. Both hLen and sLen are the size of the + * hash in TLS. Reasonable RSA key sizes are large enough for the largest + * defined RSASSA-PSS algorithm, but 1024-bit RSA is slightly too large for + * SHA-512. 1024-bit RSA is sometimes used for test credentials, so check + * the size to fall back to another algorithm. */ + if (ssl_private_key_max_signature_len(ssl) < 2 * EVP_MD_size(md) + 2) { + return 0; + } + + /* RSA-PSS is only supported by message-based private keys. */ + if (ssl->cert->key_method != NULL && ssl->cert->key_method->sign == NULL) { + return 0; + } + + return 1; + } + + return 0; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_privkey_cc.cc b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_privkey_cc.cc new file mode 100644 index 000000000..653308c12 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_privkey_cc.cc @@ -0,0 +1,76 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] */ + +#include <openssl/ssl.h> + +#include <openssl/err.h> +#include <openssl/rsa.h> + + +/* This function has been converted to C++ to check if all of libssl's + * consumers' toolchains are capable of handling C++11. Once all problems in + * consumer toolchains are found and fixed, we will convert the rest of + * libssl. */ + +int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) { + bssl::UniquePtr<RSA> rsa(RSA_private_key_from_bytes(der, der_len)); + if (!rsa) { + OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB); + return 0; + } + + return SSL_use_RSAPrivateKey(ssl, rsa.get()); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_session.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_session.c new file mode 100644 index 000000000..05ae05973 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_session.c @@ -0,0 +1,1147 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2006 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/err.h> +#include <openssl/lhash.h> +#include <openssl/mem.h> +#include <openssl/rand.h> + +#include "internal.h" +#include "../crypto/internal.h" + + +/* The address of this is a magic value, a pointer to which is returned by + * SSL_magic_pending_session_ptr(). It allows a session callback to indicate + * that it needs to asynchronously fetch session information. */ +static const char g_pending_session_magic = 0; + +static CRYPTO_EX_DATA_CLASS g_ex_data_class = + CRYPTO_EX_DATA_CLASS_INIT_WITH_APP_DATA; + +static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *session); +static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session); +static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock); + +SSL_SESSION *ssl_session_new(const SSL_X509_METHOD *x509_method) { + SSL_SESSION *session = OPENSSL_malloc(sizeof(SSL_SESSION)); + if (session == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + OPENSSL_memset(session, 0, sizeof(SSL_SESSION)); + + session->x509_method = x509_method; + session->verify_result = X509_V_ERR_INVALID_CALL; + session->references = 1; + session->timeout = SSL_DEFAULT_SESSION_TIMEOUT; + session->auth_timeout = SSL_DEFAULT_SESSION_TIMEOUT; + session->time = time(NULL); + CRYPTO_new_ex_data(&session->ex_data); + return session; +} + +SSL_SESSION *SSL_SESSION_new(const SSL_CTX *ctx) { + return ssl_session_new(ctx->x509_method); +} + +SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session, int dup_flags) { + SSL_SESSION *new_session = ssl_session_new(session->x509_method); + if (new_session == NULL) { + goto err; + } + + new_session->is_server = session->is_server; + new_session->ssl_version = session->ssl_version; + new_session->sid_ctx_length = session->sid_ctx_length; + OPENSSL_memcpy(new_session->sid_ctx, session->sid_ctx, session->sid_ctx_length); + + /* Copy the key material. */ + new_session->master_key_length = session->master_key_length; + OPENSSL_memcpy(new_session->master_key, session->master_key, + session->master_key_length); + new_session->cipher = session->cipher; + + /* Copy authentication state. */ + if (session->psk_identity != NULL) { + new_session->psk_identity = BUF_strdup(session->psk_identity); + if (new_session->psk_identity == NULL) { + goto err; + } + } + if (session->certs != NULL) { + new_session->certs = sk_CRYPTO_BUFFER_new_null(); + if (new_session->certs == NULL) { + goto err; + } + for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(session->certs); i++) { + CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(session->certs, i); + if (!sk_CRYPTO_BUFFER_push(new_session->certs, buffer)) { + goto err; + } + CRYPTO_BUFFER_up_ref(buffer); + } + } + + if (!session->x509_method->session_dup(new_session, session)) { + goto err; + } + + new_session->verify_result = session->verify_result; + + new_session->ocsp_response_length = session->ocsp_response_length; + if (session->ocsp_response != NULL) { + new_session->ocsp_response = BUF_memdup(session->ocsp_response, + session->ocsp_response_length); + if (new_session->ocsp_response == NULL) { + goto err; + } + } + + new_session->tlsext_signed_cert_timestamp_list_length = + session->tlsext_signed_cert_timestamp_list_length; + if (session->tlsext_signed_cert_timestamp_list != NULL) { + new_session->tlsext_signed_cert_timestamp_list = + BUF_memdup(session->tlsext_signed_cert_timestamp_list, + session->tlsext_signed_cert_timestamp_list_length); + if (new_session->tlsext_signed_cert_timestamp_list == NULL) { + goto err; + } + } + + OPENSSL_memcpy(new_session->peer_sha256, session->peer_sha256, + SHA256_DIGEST_LENGTH); + new_session->peer_sha256_valid = session->peer_sha256_valid; + + if (session->tlsext_hostname != NULL) { + new_session->tlsext_hostname = BUF_strdup(session->tlsext_hostname); + if (new_session->tlsext_hostname == NULL) { + goto err; + } + } + + new_session->peer_signature_algorithm = session->peer_signature_algorithm; + + new_session->timeout = session->timeout; + new_session->auth_timeout = session->auth_timeout; + new_session->time = session->time; + + /* Copy non-authentication connection properties. */ + if (dup_flags & SSL_SESSION_INCLUDE_NONAUTH) { + new_session->session_id_length = session->session_id_length; + OPENSSL_memcpy(new_session->session_id, session->session_id, + session->session_id_length); + + new_session->group_id = session->group_id; + + OPENSSL_memcpy(new_session->original_handshake_hash, + session->original_handshake_hash, + session->original_handshake_hash_len); + new_session->original_handshake_hash_len = + session->original_handshake_hash_len; + new_session->tlsext_tick_lifetime_hint = session->tlsext_tick_lifetime_hint; + new_session->ticket_age_add = session->ticket_age_add; + new_session->ticket_max_early_data = session->ticket_max_early_data; + new_session->extended_master_secret = session->extended_master_secret; + + if (session->early_alpn != NULL) { + new_session->early_alpn = + BUF_memdup(session->early_alpn, session->early_alpn_len); + if (new_session->early_alpn == NULL) { + goto err; + } + } + new_session->early_alpn_len = session->early_alpn_len; + } + + /* Copy the ticket. */ + if (dup_flags & SSL_SESSION_INCLUDE_TICKET) { + if (session->tlsext_tick != NULL) { + new_session->tlsext_tick = + BUF_memdup(session->tlsext_tick, session->tlsext_ticklen); + if (new_session->tlsext_tick == NULL) { + goto err; + } + } + new_session->tlsext_ticklen = session->tlsext_ticklen; + } + + /* The new_session does not get a copy of the ex_data. */ + + new_session->not_resumable = 1; + return new_session; + +err: + SSL_SESSION_free(new_session); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; +} + +void ssl_session_rebase_time(SSL *ssl, SSL_SESSION *session) { + struct OPENSSL_timeval now; + ssl_get_current_time(ssl, &now); + + /* To avoid overflows and underflows, if we've gone back in time, update the + * time, but mark the session expired. */ + if (session->time > now.tv_sec) { + session->time = now.tv_sec; + session->timeout = 0; + session->auth_timeout = 0; + return; + } + + /* Adjust the session time and timeouts. If the session has already expired, + * clamp the timeouts at zero. */ + uint64_t delta = now.tv_sec - session->time; + session->time = now.tv_sec; + if (session->timeout < delta) { + session->timeout = 0; + } else { + session->timeout -= delta; + } + if (session->auth_timeout < delta) { + session->auth_timeout = 0; + } else { + session->auth_timeout -= delta; + } +} + +void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session, + uint32_t timeout) { + /* Rebase the timestamp relative to the current time so |timeout| is measured + * correctly. */ + ssl_session_rebase_time(ssl, session); + + if (session->timeout > timeout) { + return; + } + + session->timeout = timeout; + if (session->timeout > session->auth_timeout) { + session->timeout = session->auth_timeout; + } +} + +int SSL_SESSION_up_ref(SSL_SESSION *session) { + CRYPTO_refcount_inc(&session->references); + return 1; +} + +void SSL_SESSION_free(SSL_SESSION *session) { + if (session == NULL || + !CRYPTO_refcount_dec_and_test_zero(&session->references)) { + return; + } + + CRYPTO_free_ex_data(&g_ex_data_class, session, &session->ex_data); + + OPENSSL_cleanse(session->master_key, sizeof(session->master_key)); + OPENSSL_cleanse(session->session_id, sizeof(session->session_id)); + sk_CRYPTO_BUFFER_pop_free(session->certs, CRYPTO_BUFFER_free); + session->x509_method->session_clear(session); + OPENSSL_free(session->tlsext_hostname); + OPENSSL_free(session->tlsext_tick); + OPENSSL_free(session->tlsext_signed_cert_timestamp_list); + OPENSSL_free(session->ocsp_response); + OPENSSL_free(session->psk_identity); + OPENSSL_free(session->early_alpn); + OPENSSL_cleanse(session, sizeof(*session)); + OPENSSL_free(session); +} + +const uint8_t *SSL_SESSION_get_id(const SSL_SESSION *session, + unsigned *out_len) { + if (out_len != NULL) { + *out_len = session->session_id_length; + } + return session->session_id; +} + +uint32_t SSL_SESSION_get_timeout(const SSL_SESSION *session) { + return session->timeout; +} + +uint64_t SSL_SESSION_get_time(const SSL_SESSION *session) { + if (session == NULL) { + /* NULL should crash, but silently accept it here for compatibility. */ + return 0; + } + return session->time; +} + +X509 *SSL_SESSION_get0_peer(const SSL_SESSION *session) { + return session->x509_peer; +} + +size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, uint8_t *out, + size_t max_out) { + /* TODO(davidben): Fix master_key_length's type and remove these casts. */ + if (max_out == 0) { + return (size_t)session->master_key_length; + } + if (max_out > (size_t)session->master_key_length) { + max_out = (size_t)session->master_key_length; + } + OPENSSL_memcpy(out, session->master_key, max_out); + return max_out; +} + +uint64_t SSL_SESSION_set_time(SSL_SESSION *session, uint64_t time) { + if (session == NULL) { + return 0; + } + + session->time = time; + return time; +} + +uint32_t SSL_SESSION_set_timeout(SSL_SESSION *session, uint32_t timeout) { + if (session == NULL) { + return 0; + } + + session->timeout = timeout; + session->auth_timeout = timeout; + return 1; +} + +int SSL_SESSION_set1_id_context(SSL_SESSION *session, const uint8_t *sid_ctx, + size_t sid_ctx_len) { + if (sid_ctx_len > sizeof(session->sid_ctx)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); + return 0; + } + + assert(sizeof(session->sid_ctx) < 256); + session->sid_ctx_length = (uint8_t)sid_ctx_len; + OPENSSL_memcpy(session->sid_ctx, sid_ctx, sid_ctx_len); + + return 1; +} + +SSL_SESSION *SSL_magic_pending_session_ptr(void) { + return (SSL_SESSION *)&g_pending_session_magic; +} + +SSL_SESSION *SSL_get_session(const SSL *ssl) { + /* Once the handshake completes we return the established session. Otherwise + * we return the intermediate session, either |session| (for resumption) or + * |new_session| if doing a full handshake. */ + if (!SSL_in_init(ssl)) { + return ssl->s3->established_session; + } + if (ssl->s3->hs->new_session != NULL) { + return ssl->s3->hs->new_session; + } + return ssl->session; +} + +SSL_SESSION *SSL_get1_session(SSL *ssl) { + SSL_SESSION *ret = SSL_get_session(ssl); + if (ret != NULL) { + SSL_SESSION_up_ref(ret); + } + return ret; +} + +int SSL_SESSION_get_ex_new_index(long argl, void *argp, + CRYPTO_EX_unused *unused, + CRYPTO_EX_dup *dup_func, + CRYPTO_EX_free *free_func) { + int index; + if (!CRYPTO_get_ex_new_index(&g_ex_data_class, &index, argl, argp, dup_func, + free_func)) { + return -1; + } + return index; +} + +int SSL_SESSION_set_ex_data(SSL_SESSION *session, int idx, void *arg) { + return CRYPTO_set_ex_data(&session->ex_data, idx, arg); +} + +void *SSL_SESSION_get_ex_data(const SSL_SESSION *session, int idx) { + return CRYPTO_get_ex_data(&session->ex_data, idx); +} + +const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session, + const SSL *ssl) { + uint16_t version; + if (!ssl->method->version_from_wire(&version, session->ssl_version)) { + return NULL; + } + + return ssl_get_handshake_digest(session->cipher->algorithm_prf, version); +} + +int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) { + SSL *const ssl = hs->ssl; + if (ssl->mode & SSL_MODE_NO_SESSION_CREATION) { + OPENSSL_PUT_ERROR(SSL, SSL_R_SESSION_MAY_NOT_BE_CREATED); + return 0; + } + + SSL_SESSION *session = ssl_session_new(ssl->ctx->x509_method); + if (session == NULL) { + return 0; + } + + session->is_server = is_server; + session->ssl_version = ssl->version; + + /* Fill in the time from the |SSL_CTX|'s clock. */ + struct OPENSSL_timeval now; + ssl_get_current_time(ssl, &now); + session->time = now.tv_sec; + + uint16_t version = ssl3_protocol_version(ssl); + if (version >= TLS1_3_VERSION) { + /* TLS 1.3 uses tickets as authenticators, so we are willing to use them for + * longer. */ + session->timeout = ssl->session_ctx->session_psk_dhe_timeout; + session->auth_timeout = SSL_DEFAULT_SESSION_AUTH_TIMEOUT; + } else { + /* TLS 1.2 resumption does not incorporate new key material, so we use a + * much shorter timeout. */ + session->timeout = ssl->session_ctx->session_timeout; + session->auth_timeout = ssl->session_ctx->session_timeout; + } + + if (is_server) { + if (hs->ticket_expected || version >= TLS1_3_VERSION) { + /* Don't set session IDs for sessions resumed with tickets. This will keep + * them out of the session cache. */ + session->session_id_length = 0; + } else { + session->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; + if (!RAND_bytes(session->session_id, session->session_id_length)) { + goto err; + } + } + } else { + session->session_id_length = 0; + } + + if (ssl->cert->sid_ctx_length > sizeof(session->sid_ctx)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + OPENSSL_memcpy(session->sid_ctx, ssl->cert->sid_ctx, + ssl->cert->sid_ctx_length); + session->sid_ctx_length = ssl->cert->sid_ctx_length; + + /* The session is marked not resumable until it is completely filled in. */ + session->not_resumable = 1; + session->verify_result = X509_V_ERR_INVALID_CALL; + + SSL_SESSION_free(hs->new_session); + hs->new_session = session; + ssl_set_session(ssl, NULL); + return 1; + +err: + SSL_SESSION_free(session); + return 0; +} + +static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out, + const uint8_t *session_buf, + size_t session_len) { + int ret = 0; + + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + HMAC_CTX hctx; + HMAC_CTX_init(&hctx); + + /* If the session is too long, emit a dummy value rather than abort the + * connection. */ + static const size_t kMaxTicketOverhead = + 16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE; + if (session_len > 0xffff - kMaxTicketOverhead) { + static const char kTicketPlaceholder[] = "TICKET TOO LARGE"; + if (CBB_add_bytes(out, (const uint8_t *)kTicketPlaceholder, + strlen(kTicketPlaceholder))) { + ret = 1; + } + goto err; + } + + /* Initialize HMAC and cipher contexts. If callback present it does all the + * work otherwise use generated values from parent ctx. */ + SSL_CTX *tctx = ssl->session_ctx; + uint8_t iv[EVP_MAX_IV_LENGTH]; + uint8_t key_name[16]; + if (tctx->tlsext_ticket_key_cb != NULL) { + if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx, + 1 /* encrypt */) < 0) { + goto err; + } + } else { + if (!RAND_bytes(iv, 16) || + !EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, + tctx->tlsext_tick_aes_key, iv) || + !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(), + NULL)) { + goto err; + } + OPENSSL_memcpy(key_name, tctx->tlsext_tick_key_name, 16); + } + + uint8_t *ptr; + if (!CBB_add_bytes(out, key_name, 16) || + !CBB_add_bytes(out, iv, EVP_CIPHER_CTX_iv_length(&ctx)) || + !CBB_reserve(out, &ptr, session_len + EVP_MAX_BLOCK_LENGTH)) { + goto err; + } + + size_t total = 0; +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + OPENSSL_memcpy(ptr, session_buf, session_len); + total = session_len; +#else + int len; + if (!EVP_EncryptUpdate(&ctx, ptr + total, &len, session_buf, session_len)) { + goto err; + } + total += len; + if (!EVP_EncryptFinal_ex(&ctx, ptr + total, &len)) { + goto err; + } + total += len; +#endif + if (!CBB_did_write(out, total)) { + goto err; + } + + unsigned hlen; + if (!HMAC_Update(&hctx, CBB_data(out), CBB_len(out)) || + !CBB_reserve(out, &ptr, EVP_MAX_MD_SIZE) || + !HMAC_Final(&hctx, ptr, &hlen) || + !CBB_did_write(out, hlen)) { + goto err; + } + + ret = 1; + +err: + EVP_CIPHER_CTX_cleanup(&ctx); + HMAC_CTX_cleanup(&hctx); + return ret; +} + +static int ssl_encrypt_ticket_with_method(SSL *ssl, CBB *out, + const uint8_t *session_buf, + size_t session_len) { + const SSL_TICKET_AEAD_METHOD *method = ssl->session_ctx->ticket_aead_method; + const size_t max_overhead = method->max_overhead(ssl); + const size_t max_out = session_len + max_overhead; + if (max_out < max_overhead) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + + uint8_t *ptr; + if (!CBB_reserve(out, &ptr, max_out)) { + return 0; + } + + size_t out_len; + if (!method->seal(ssl, ptr, &out_len, max_out, session_buf, session_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_TICKET_ENCRYPTION_FAILED); + return 0; + } + + if (!CBB_did_write(out, out_len)) { + return 0; + } + + return 1; +} + +int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) { + /* Serialize the SSL_SESSION to be encoded into the ticket. */ + uint8_t *session_buf = NULL; + size_t session_len; + if (!SSL_SESSION_to_bytes_for_ticket(session, &session_buf, &session_len)) { + return -1; + } + + int ret = 0; + if (ssl->session_ctx->ticket_aead_method) { + ret = ssl_encrypt_ticket_with_method(ssl, out, session_buf, session_len); + } else { + ret = + ssl_encrypt_ticket_with_cipher_ctx(ssl, out, session_buf, session_len); + } + + OPENSSL_free(session_buf); + return ret; +} + +int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session) { + if (session == NULL) { + return 0; + } + + return session->sid_ctx_length == ssl->cert->sid_ctx_length && + OPENSSL_memcmp(session->sid_ctx, ssl->cert->sid_ctx, + ssl->cert->sid_ctx_length) == 0; +} + +int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session) { + if (session == NULL) { + return 0; + } + + struct OPENSSL_timeval now; + ssl_get_current_time(ssl, &now); + + /* Reject tickets from the future to avoid underflow. */ + if (now.tv_sec < session->time) { + return 0; + } + + return session->timeout > now.tv_sec - session->time; +} + +int ssl_session_is_resumable(const SSL_HANDSHAKE *hs, + const SSL_SESSION *session) { + const SSL *const ssl = hs->ssl; + return ssl_session_is_context_valid(ssl, session) && + /* The session must have been created by the same type of end point as + * we're now using it with. */ + ssl->server == session->is_server && + /* The session must not be expired. */ + ssl_session_is_time_valid(ssl, session) && + /* Only resume if the session's version matches the negotiated + * version. */ + ssl->version == session->ssl_version && + /* Only resume if the session's cipher matches the negotiated one. */ + hs->new_cipher == session->cipher && + /* If the session contains a client certificate (either the full + * certificate or just the hash) then require that the form of the + * certificate matches the current configuration. */ + ((sk_CRYPTO_BUFFER_num(session->certs) == 0 && + !session->peer_sha256_valid) || + session->peer_sha256_valid == + ssl->retain_only_sha256_of_client_certs); +} + +/* ssl_lookup_session looks up |session_id| in the session cache and sets + * |*out_session| to an |SSL_SESSION| object if found. The caller takes + * ownership of the result. */ +static enum ssl_session_result_t ssl_lookup_session( + SSL *ssl, SSL_SESSION **out_session, const uint8_t *session_id, + size_t session_id_len) { + *out_session = NULL; + + if (session_id_len == 0 || session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { + return ssl_session_success; + } + + SSL_SESSION *session = NULL; + /* Try the internal cache, if it exists. */ + if (!(ssl->session_ctx->session_cache_mode & + SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) { + SSL_SESSION data; + data.ssl_version = ssl->version; + data.session_id_length = session_id_len; + OPENSSL_memcpy(data.session_id, session_id, session_id_len); + + CRYPTO_MUTEX_lock_read(&ssl->session_ctx->lock); + session = lh_SSL_SESSION_retrieve(ssl->session_ctx->sessions, &data); + if (session != NULL) { + SSL_SESSION_up_ref(session); + } + /* TODO(davidben): This should probably move it to the front of the list. */ + CRYPTO_MUTEX_unlock_read(&ssl->session_ctx->lock); + } + + /* Fall back to the external cache, if it exists. */ + if (session == NULL && + ssl->session_ctx->get_session_cb != NULL) { + int copy = 1; + session = ssl->session_ctx->get_session_cb(ssl, (uint8_t *)session_id, + session_id_len, ©); + + if (session == NULL) { + return ssl_session_success; + } + + if (session == SSL_magic_pending_session_ptr()) { + return ssl_session_retry; + } + + /* Increment reference count now if the session callback asks us to do so + * (note that if the session structures returned by the callback are shared + * between threads, it must handle the reference count itself [i.e. copy == + * 0], or things won't be thread-safe). */ + if (copy) { + SSL_SESSION_up_ref(session); + } + + /* Add the externally cached session to the internal cache if necessary. */ + if (!(ssl->session_ctx->session_cache_mode & + SSL_SESS_CACHE_NO_INTERNAL_STORE)) { + SSL_CTX_add_session(ssl->session_ctx, session); + } + } + + if (session != NULL && + !ssl_session_is_time_valid(ssl, session)) { + /* The session was from the cache, so remove it. */ + SSL_CTX_remove_session(ssl->session_ctx, session); + SSL_SESSION_free(session); + session = NULL; + } + + *out_session = session; + return ssl_session_success; +} + +enum ssl_session_result_t ssl_get_prev_session( + SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported, + int *out_renew_ticket, const SSL_CLIENT_HELLO *client_hello) { + /* This is used only by servers. */ + assert(ssl->server); + SSL_SESSION *session = NULL; + int renew_ticket = 0; + + /* If tickets are disabled, always behave as if no tickets are present. */ + const uint8_t *ticket = NULL; + size_t ticket_len = 0; + const int tickets_supported = + !(SSL_get_options(ssl) & SSL_OP_NO_TICKET) && + ssl->version > SSL3_VERSION && + SSL_early_callback_ctx_extension_get( + client_hello, TLSEXT_TYPE_session_ticket, &ticket, &ticket_len); + if (tickets_supported && ticket_len > 0) { + switch (ssl_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len, + client_hello->session_id, + client_hello->session_id_len)) { + case ssl_ticket_aead_success: + break; + case ssl_ticket_aead_ignore_ticket: + assert(session == NULL); + break; + case ssl_ticket_aead_error: + return ssl_session_error; + case ssl_ticket_aead_retry: + return ssl_session_ticket_retry; + } + } else { + /* The client didn't send a ticket, so the session ID is a real ID. */ + enum ssl_session_result_t lookup_ret = ssl_lookup_session( + ssl, &session, client_hello->session_id, client_hello->session_id_len); + if (lookup_ret != ssl_session_success) { + return lookup_ret; + } + } + + *out_session = session; + *out_tickets_supported = tickets_supported; + *out_renew_ticket = renew_ticket; + return ssl_session_success; +} + +int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *session) { + /* Although |session| is inserted into two structures (a doubly-linked list + * and the hash table), |ctx| only takes one reference. */ + SSL_SESSION_up_ref(session); + + SSL_SESSION *old_session; + CRYPTO_MUTEX_lock_write(&ctx->lock); + if (!lh_SSL_SESSION_insert(ctx->sessions, &old_session, session)) { + CRYPTO_MUTEX_unlock_write(&ctx->lock); + SSL_SESSION_free(session); + return 0; + } + + if (old_session != NULL) { + if (old_session == session) { + /* |session| was already in the cache. */ + CRYPTO_MUTEX_unlock_write(&ctx->lock); + SSL_SESSION_free(old_session); + return 0; + } + + /* There was a session ID collision. |old_session| must be removed from + * the linked list and released. */ + SSL_SESSION_list_remove(ctx, old_session); + SSL_SESSION_free(old_session); + } + + SSL_SESSION_list_add(ctx, session); + + /* Enforce any cache size limits. */ + if (SSL_CTX_sess_get_cache_size(ctx) > 0) { + while (SSL_CTX_sess_number(ctx) > SSL_CTX_sess_get_cache_size(ctx)) { + if (!remove_session_lock(ctx, ctx->session_cache_tail, 0)) { + break; + } + } + } + + CRYPTO_MUTEX_unlock_write(&ctx->lock); + return 1; +} + +int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *session) { + return remove_session_lock(ctx, session, 1); +} + +static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock) { + int ret = 0; + + if (session != NULL && session->session_id_length != 0) { + if (lock) { + CRYPTO_MUTEX_lock_write(&ctx->lock); + } + SSL_SESSION *found_session = lh_SSL_SESSION_retrieve(ctx->sessions, + session); + if (found_session == session) { + ret = 1; + found_session = lh_SSL_SESSION_delete(ctx->sessions, session); + SSL_SESSION_list_remove(ctx, session); + } + + if (lock) { + CRYPTO_MUTEX_unlock_write(&ctx->lock); + } + + if (ret) { + found_session->not_resumable = 1; + if (ctx->remove_session_cb != NULL) { + ctx->remove_session_cb(ctx, found_session); + } + SSL_SESSION_free(found_session); + } + } + + return ret; +} + +int SSL_set_session(SSL *ssl, SSL_SESSION *session) { + /* SSL_set_session may only be called before the handshake has started. */ + if (ssl->s3->initial_handshake_complete || + ssl->s3->hs == NULL || + ssl->s3->hs->state != SSL_ST_INIT) { + abort(); + } + + ssl_set_session(ssl, session); + return 1; +} + +void ssl_set_session(SSL *ssl, SSL_SESSION *session) { + if (ssl->session == session) { + return; + } + + SSL_SESSION_free(ssl->session); + ssl->session = session; + if (session != NULL) { + SSL_SESSION_up_ref(session); + } +} + +uint32_t SSL_CTX_set_timeout(SSL_CTX *ctx, uint32_t timeout) { + if (ctx == NULL) { + return 0; + } + + /* Historically, zero was treated as |SSL_DEFAULT_SESSION_TIMEOUT|. */ + if (timeout == 0) { + timeout = SSL_DEFAULT_SESSION_TIMEOUT; + } + + uint32_t old_timeout = ctx->session_timeout; + ctx->session_timeout = timeout; + return old_timeout; +} + +uint32_t SSL_CTX_get_timeout(const SSL_CTX *ctx) { + if (ctx == NULL) { + return 0; + } + + return ctx->session_timeout; +} + +void SSL_CTX_set_session_psk_dhe_timeout(SSL_CTX *ctx, uint32_t timeout) { + ctx->session_psk_dhe_timeout = timeout; +} + +typedef struct timeout_param_st { + SSL_CTX *ctx; + uint64_t time; + LHASH_OF(SSL_SESSION) *cache; +} TIMEOUT_PARAM; + +static void timeout_doall_arg(SSL_SESSION *session, void *void_param) { + TIMEOUT_PARAM *param = void_param; + + if (param->time == 0 || + session->time + session->timeout < session->time || + param->time > (session->time + session->timeout)) { + /* timeout */ + /* The reason we don't call SSL_CTX_remove_session() is to + * save on locking overhead */ + (void) lh_SSL_SESSION_delete(param->cache, session); + SSL_SESSION_list_remove(param->ctx, session); + session->not_resumable = 1; + if (param->ctx->remove_session_cb != NULL) { + param->ctx->remove_session_cb(param->ctx, session); + } + SSL_SESSION_free(session); + } +} + +void SSL_CTX_flush_sessions(SSL_CTX *ctx, uint64_t time) { + TIMEOUT_PARAM tp; + + tp.ctx = ctx; + tp.cache = ctx->sessions; + if (tp.cache == NULL) { + return; + } + tp.time = time; + CRYPTO_MUTEX_lock_write(&ctx->lock); + lh_SSL_SESSION_doall_arg(tp.cache, timeout_doall_arg, &tp); + CRYPTO_MUTEX_unlock_write(&ctx->lock); +} + +/* locked by SSL_CTX in the calling function */ +static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *session) { + if (session->next == NULL || session->prev == NULL) { + return; + } + + if (session->next == (SSL_SESSION *)&ctx->session_cache_tail) { + /* last element in list */ + if (session->prev == (SSL_SESSION *)&ctx->session_cache_head) { + /* only one element in list */ + ctx->session_cache_head = NULL; + ctx->session_cache_tail = NULL; + } else { + ctx->session_cache_tail = session->prev; + session->prev->next = (SSL_SESSION *)&(ctx->session_cache_tail); + } + } else { + if (session->prev == (SSL_SESSION *)&ctx->session_cache_head) { + /* first element in list */ + ctx->session_cache_head = session->next; + session->next->prev = (SSL_SESSION *)&(ctx->session_cache_head); + } else { /* middle of list */ + session->next->prev = session->prev; + session->prev->next = session->next; + } + } + session->prev = session->next = NULL; +} + +static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session) { + if (session->next != NULL && session->prev != NULL) { + SSL_SESSION_list_remove(ctx, session); + } + + if (ctx->session_cache_head == NULL) { + ctx->session_cache_head = session; + ctx->session_cache_tail = session; + session->prev = (SSL_SESSION *)&(ctx->session_cache_head); + session->next = (SSL_SESSION *)&(ctx->session_cache_tail); + } else { + session->next = ctx->session_cache_head; + session->next->prev = session; + session->prev = (SSL_SESSION *)&(ctx->session_cache_head); + ctx->session_cache_head = session; + } +} + +void SSL_CTX_sess_set_new_cb(SSL_CTX *ctx, + int (*cb)(SSL *ssl, SSL_SESSION *session)) { + ctx->new_session_cb = cb; +} + +int (*SSL_CTX_sess_get_new_cb(SSL_CTX *ctx))(SSL *ssl, SSL_SESSION *session) { + return ctx->new_session_cb; +} + +void SSL_CTX_sess_set_remove_cb( + SSL_CTX *ctx, void (*cb)(SSL_CTX *ctx, SSL_SESSION *session)) { + ctx->remove_session_cb = cb; +} + +void (*SSL_CTX_sess_get_remove_cb(SSL_CTX *ctx))(SSL_CTX *ctx, + SSL_SESSION *session) { + return ctx->remove_session_cb; +} + +void SSL_CTX_sess_set_get_cb(SSL_CTX *ctx, + SSL_SESSION *(*cb)(SSL *ssl, + uint8_t *id, int id_len, + int *out_copy)) { + ctx->get_session_cb = cb; +} + +SSL_SESSION *(*SSL_CTX_sess_get_get_cb(SSL_CTX *ctx))( + SSL *ssl, uint8_t *id, int id_len, int *out_copy) { + return ctx->get_session_cb; +} + +void SSL_CTX_set_info_callback( + SSL_CTX *ctx, void (*cb)(const SSL *ssl, int type, int value)) { + ctx->info_callback = cb; +} + +void (*SSL_CTX_get_info_callback(SSL_CTX *ctx))(const SSL *ssl, int type, + int value) { + return ctx->info_callback; +} + +void SSL_CTX_set_channel_id_cb(SSL_CTX *ctx, + void (*cb)(SSL *ssl, EVP_PKEY **pkey)) { + ctx->channel_id_cb = cb; +} + +void (*SSL_CTX_get_channel_id_cb(SSL_CTX *ctx))(SSL *ssl, EVP_PKEY **pkey) { + return ctx->channel_id_cb; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_stat.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_stat.c new file mode 100644 index 000000000..571b4a9a2 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_stat.c @@ -0,0 +1,443 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. + */ + +#include <openssl/ssl.h> + +#include <assert.h> + +#include "internal.h" + + +static int ssl_state(const SSL *ssl) { + if (ssl->s3->hs == NULL) { + assert(ssl->s3->initial_handshake_complete); + return SSL_ST_OK; + } + + return ssl->s3->hs->state; +} + +const char *SSL_state_string_long(const SSL *ssl) { + switch (ssl_state(ssl)) { + case SSL_ST_ACCEPT: + return "before accept initialization"; + + case SSL_ST_CONNECT: + return "before connect initialization"; + + case SSL_ST_OK: + return "SSL negotiation finished successfully"; + + case SSL_ST_RENEGOTIATE: + return "SSL renegotiate ciphers"; + + /* SSLv3 additions */ + case SSL3_ST_CW_CLNT_HELLO_A: + return "SSLv3 write client hello A"; + + case SSL3_ST_CR_SRVR_HELLO_A: + return "SSLv3 read server hello A"; + + case SSL3_ST_CR_CERT_A: + return "SSLv3 read server certificate A"; + + case SSL3_ST_CR_KEY_EXCH_A: + return "SSLv3 read server key exchange A"; + + case SSL3_ST_CR_CERT_REQ_A: + return "SSLv3 read server certificate request A"; + + case SSL3_ST_CR_SESSION_TICKET_A: + return "SSLv3 read server session ticket A"; + + case SSL3_ST_CR_SRVR_DONE_A: + return "SSLv3 read server done A"; + + case SSL3_ST_CW_CERT_A: + return "SSLv3 write client certificate A"; + + case SSL3_ST_CW_KEY_EXCH_A: + return "SSLv3 write client key exchange A"; + + case SSL3_ST_CW_CERT_VRFY_A: + return "SSLv3 write certificate verify A"; + + case SSL3_ST_CW_CERT_VRFY_B: + return "SSLv3 write certificate verify B"; + + case SSL3_ST_CW_CHANGE: + case SSL3_ST_SW_CHANGE: + return "SSLv3 write change cipher spec"; + + case SSL3_ST_CW_FINISHED_A: + case SSL3_ST_SW_FINISHED_A: + return "SSLv3 write finished A"; + + case SSL3_ST_CR_CHANGE: + case SSL3_ST_SR_CHANGE: + return "SSLv3 read change cipher spec"; + + case SSL3_ST_CR_FINISHED_A: + case SSL3_ST_SR_FINISHED_A: + return "SSLv3 read finished A"; + + case SSL3_ST_CW_FLUSH: + case SSL3_ST_SW_FLUSH: + return "SSLv3 flush data"; + + case SSL3_ST_SR_CLNT_HELLO_A: + return "SSLv3 read client hello A"; + + case SSL3_ST_SR_CLNT_HELLO_B: + return "SSLv3 read client hello B"; + + case SSL3_ST_SR_CLNT_HELLO_C: + return "SSLv3 read client hello C"; + + case SSL3_ST_SW_SRVR_HELLO_A: + return "SSLv3 write server hello A"; + + case SSL3_ST_SW_CERT_A: + return "SSLv3 write certificate A"; + + case SSL3_ST_SW_KEY_EXCH_A: + return "SSLv3 write key exchange A"; + + case SSL3_ST_SW_CERT_REQ_A: + return "SSLv3 write certificate request A"; + + case SSL3_ST_SW_SESSION_TICKET_A: + return "SSLv3 write session ticket A"; + + case SSL3_ST_SW_SRVR_DONE_A: + return "SSLv3 write server done A"; + + case SSL3_ST_SR_CERT_A: + return "SSLv3 read client certificate A"; + + case SSL3_ST_SR_KEY_EXCH_A: + return "SSLv3 read client key exchange A"; + + case SSL3_ST_SR_KEY_EXCH_B: + return "SSLv3 read client key exchange B"; + + case SSL3_ST_SR_CERT_VRFY_A: + return "SSLv3 read certificate verify A"; + + /* DTLS */ + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: + return "DTLS1 read hello verify request A"; + + default: + return "unknown state"; + } +} + +const char *SSL_state_string(const SSL *ssl) { + switch (ssl_state(ssl)) { + case SSL_ST_ACCEPT: + return "AINIT "; + + case SSL_ST_CONNECT: + return "CINIT "; + + case SSL_ST_OK: + return "SSLOK "; + + /* SSLv3 additions */ + case SSL3_ST_SW_FLUSH: + case SSL3_ST_CW_FLUSH: + return "3FLUSH"; + + case SSL3_ST_CW_CLNT_HELLO_A: + return "3WCH_A"; + + case SSL3_ST_CR_SRVR_HELLO_A: + return "3RSH_A"; + + case SSL3_ST_CR_CERT_A: + return "3RSC_A"; + + case SSL3_ST_CR_KEY_EXCH_A: + return "3RSKEA"; + + case SSL3_ST_CR_CERT_REQ_A: + return "3RCR_A"; + + case SSL3_ST_CR_SRVR_DONE_A: + return "3RSD_A"; + + case SSL3_ST_CW_CERT_A: + return "3WCC_A"; + + case SSL3_ST_CW_KEY_EXCH_A: + return "3WCKEA"; + + case SSL3_ST_CW_CERT_VRFY_A: + return "3WCV_A"; + + case SSL3_ST_CW_CERT_VRFY_B: + return "3WCV_B"; + + case SSL3_ST_SW_CHANGE: + case SSL3_ST_CW_CHANGE: + return "3WCCS_"; + + case SSL3_ST_SW_FINISHED_A: + case SSL3_ST_CW_FINISHED_A: + return "3WFINA"; + + case SSL3_ST_CR_CHANGE: + case SSL3_ST_SR_CHANGE: + return "3RCCS_"; + + case SSL3_ST_SR_FINISHED_A: + case SSL3_ST_CR_FINISHED_A: + return "3RFINA"; + + case SSL3_ST_SR_CLNT_HELLO_A: + return "3RCH_A"; + + case SSL3_ST_SR_CLNT_HELLO_B: + return "3RCH_B"; + + case SSL3_ST_SR_CLNT_HELLO_C: + return "3RCH_C"; + + case SSL3_ST_SW_SRVR_HELLO_A: + return "3WSH_A"; + + case SSL3_ST_SW_CERT_A: + return "3WSC_A"; + + case SSL3_ST_SW_KEY_EXCH_A: + return "3WSKEA"; + + case SSL3_ST_SW_KEY_EXCH_B: + return "3WSKEB"; + + case SSL3_ST_SW_CERT_REQ_A: + return "3WCR_A"; + + case SSL3_ST_SW_SRVR_DONE_A: + return "3WSD_A"; + + case SSL3_ST_SR_CERT_A: + return "3RCC_A"; + + case SSL3_ST_SR_KEY_EXCH_A: + return "3RCKEA"; + + case SSL3_ST_SR_CERT_VRFY_A: + return "3RCV_A"; + + /* DTLS */ + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: + return "DRCHVA"; + + default: + return "UNKWN "; + } +} + +const char *SSL_alert_type_string_long(int value) { + value >>= 8; + if (value == SSL3_AL_WARNING) { + return "warning"; + } else if (value == SSL3_AL_FATAL) { + return "fatal"; + } + + return "unknown"; +} + +const char *SSL_alert_type_string(int value) { + return "!"; +} + +const char *SSL_alert_desc_string(int value) { + return "!!"; +} + +const char *SSL_alert_desc_string_long(int value) { + switch (value & 0xff) { + case SSL3_AD_CLOSE_NOTIFY: + return "close notify"; + + case SSL3_AD_UNEXPECTED_MESSAGE: + return "unexpected_message"; + + case SSL3_AD_BAD_RECORD_MAC: + return "bad record mac"; + + case SSL3_AD_DECOMPRESSION_FAILURE: + return "decompression failure"; + + case SSL3_AD_HANDSHAKE_FAILURE: + return "handshake failure"; + + case SSL3_AD_NO_CERTIFICATE: + return "no certificate"; + + case SSL3_AD_BAD_CERTIFICATE: + return "bad certificate"; + + case SSL3_AD_UNSUPPORTED_CERTIFICATE: + return "unsupported certificate"; + + case SSL3_AD_CERTIFICATE_REVOKED: + return "certificate revoked"; + + case SSL3_AD_CERTIFICATE_EXPIRED: + return "certificate expired"; + + case SSL3_AD_CERTIFICATE_UNKNOWN: + return "certificate unknown"; + + case SSL3_AD_ILLEGAL_PARAMETER: + return "illegal parameter"; + + case TLS1_AD_DECRYPTION_FAILED: + return "decryption failed"; + + case TLS1_AD_RECORD_OVERFLOW: + return "record overflow"; + + case TLS1_AD_UNKNOWN_CA: + return "unknown CA"; + + case TLS1_AD_ACCESS_DENIED: + return "access denied"; + + case TLS1_AD_DECODE_ERROR: + return "decode error"; + + case TLS1_AD_DECRYPT_ERROR: + return "decrypt error"; + + case TLS1_AD_EXPORT_RESTRICTION: + return "export restriction"; + + case TLS1_AD_PROTOCOL_VERSION: + return "protocol version"; + + case TLS1_AD_INSUFFICIENT_SECURITY: + return "insufficient security"; + + case TLS1_AD_INTERNAL_ERROR: + return "internal error"; + + case SSL3_AD_INAPPROPRIATE_FALLBACK: + return "inappropriate fallback"; + + case TLS1_AD_USER_CANCELLED: + return "user canceled"; + + case TLS1_AD_NO_RENEGOTIATION: + return "no renegotiation"; + + case TLS1_AD_UNSUPPORTED_EXTENSION: + return "unsupported extension"; + + case TLS1_AD_CERTIFICATE_UNOBTAINABLE: + return "certificate unobtainable"; + + case TLS1_AD_UNRECOGNIZED_NAME: + return "unrecognized name"; + + case TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + return "bad certificate status response"; + + case TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: + return "bad certificate hash value"; + + case TLS1_AD_UNKNOWN_PSK_IDENTITY: + return "unknown PSK identity"; + + case TLS1_AD_CERTIFICATE_REQUIRED: + return "certificate required"; + + default: + return "unknown"; + } +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_test.cc b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_test.cc new file mode 100644 index 000000000..6b150e8c6 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_test.cc @@ -0,0 +1,3531 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include <algorithm> +#include <string> +#include <utility> +#include <vector> + +#include <gtest/gtest.h> + +#include <openssl/base64.h> +#include <openssl/bio.h> +#include <openssl/cipher.h> +#include <openssl/crypto.h> +#include <openssl/err.h> +#include <openssl/hmac.h> +#include <openssl/pem.h> +#include <openssl/sha.h> +#include <openssl/ssl.h> +#include <openssl/rand.h> +#include <openssl/x509.h> + +#include "internal.h" +#include "../crypto/internal.h" +#include "../crypto/test/test_util.h" + +#if defined(OPENSSL_WINDOWS) +/* Windows defines struct timeval in winsock2.h. */ +OPENSSL_MSVC_PRAGMA(warning(push, 3)) +#include <winsock2.h> +OPENSSL_MSVC_PRAGMA(warning(pop)) +#else +#include <sys/time.h> +#endif + + +struct ExpectedCipher { + unsigned long id; + int in_group_flag; +}; + +struct CipherTest { + // The rule string to apply. + const char *rule; + // The list of expected ciphers, in order. + std::vector<ExpectedCipher> expected; + // True if this cipher list should fail in strict mode. + bool strict_fail; +}; + +struct CurveTest { + // The rule string to apply. + const char *rule; + // The list of expected curves, in order. + std::vector<uint16_t> expected; +}; + +static const CipherTest kCipherTests[] = { + // Selecting individual ciphers should work. + { + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256", + { + {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0}, + }, + false, + }, + // + reorders selected ciphers to the end, keeping their relative order. + { + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "+aRSA", + { + {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0}, + }, + false, + }, + // ! banishes ciphers from future selections. + { + "!aRSA:" + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256", + { + {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0}, + }, + false, + }, + // Multiple masks can be ANDed in a single rule. + { + "kRSA+AESGCM+AES128", + { + {TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0}, + }, + false, + }, + // - removes selected ciphers, but preserves their order for future + // selections. Select AES_128_GCM, but order the key exchanges RSA, DHE_RSA, + // ECDHE_RSA. + { + "ALL:-kECDHE:-kDHE:-kRSA:-ALL:" + "AESGCM+AES128+aRSA", + { + {TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0}, + {TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0}, + }, + false, + }, + // Unknown selectors are no-ops, except in strict mode. + { + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "BOGUS1", + { + {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0}, + }, + true, + }, + // Unknown selectors are no-ops, except in strict mode. + { + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "-BOGUS2:+BOGUS3:!BOGUS4", + { + {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0}, + }, + true, + }, + // Square brackets specify equi-preference groups. + { + "[ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES128-GCM-SHA256]:" + "[ECDHE-RSA-CHACHA20-POLY1305]:" + "ECDHE-RSA-AES128-GCM-SHA256", + { + {TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 1}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0}, + }, + false, + }, + // @STRENGTH performs a stable strength-sort of the selected ciphers and + // only the selected ciphers. + { + // To simplify things, banish all but {ECDHE_RSA,RSA} x + // {CHACHA20,AES_256_CBC,AES_128_CBC} x SHA1. + "!kEDH:!AESGCM:!3DES:!SHA256:!SHA384:" + // Order some ciphers backwards by strength. + "ALL:-CHACHA20:-AES256:-AES128:-ALL:" + // Select ECDHE ones and sort them by strength. Ties should resolve + // based on the order above. + "kECDHE:@STRENGTH:-ALL:" + // Now bring back everything uses RSA. ECDHE_RSA should be first, sorted + // by strength. Then RSA, backwards by strength. + "aRSA", + { + {TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0}, + {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0}, + {TLS1_CK_RSA_WITH_AES_128_SHA, 0}, + {TLS1_CK_RSA_WITH_AES_256_SHA, 0}, + }, + false, + }, + // Exact ciphers may not be used in multi-part rules; they are treated + // as unknown aliases. + { + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "!ECDHE-RSA-AES128-GCM-SHA256+RSA:" + "!ECDSA+ECDHE-ECDSA-AES128-GCM-SHA256", + { + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0}, + }, + true, + }, + // SSLv3 matches everything that existed before TLS 1.2. + { + "AES128-SHA:AES128-SHA256:!SSLv3", + { + {TLS1_CK_RSA_WITH_AES_128_SHA256, 0}, + }, + false, + }, + // TLSv1.2 matches everything added in TLS 1.2. + { + "AES128-SHA:AES128-SHA256:!TLSv1.2", + { + {TLS1_CK_RSA_WITH_AES_128_SHA, 0}, + }, + false, + }, + // The two directives have no intersection. But each component is valid, so + // even in strict mode it is accepted. + { + "AES128-SHA:AES128-SHA256:!TLSv1.2+SSLv3", + { + {TLS1_CK_RSA_WITH_AES_128_SHA, 0}, + {TLS1_CK_RSA_WITH_AES_128_SHA256, 0}, + }, + false, + }, +}; + +static const char *kBadRules[] = { + // Invalid brackets. + "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256", + "RSA]", + "[[RSA]]", + // Operators inside brackets. + "[+RSA]", + // Unknown directive. + "@BOGUS", + // Empty cipher lists error at SSL_CTX_set_cipher_list. + "", + "BOGUS", + // COMPLEMENTOFDEFAULT is empty. + "COMPLEMENTOFDEFAULT", + // Invalid command. + "?BAR", + // Special operators are not allowed if groups are used. + "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:+FOO", + "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:!FOO", + "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:-FOO", + "[ECDHE-RSA-CHACHA20-POLY1305|ECDHE-RSA-AES128-GCM-SHA256]:@STRENGTH", + // Opcode supplied, but missing selector. + "+", +}; + +static const char *kMustNotIncludeNull[] = { + "ALL", + "DEFAULT", + "HIGH", + "FIPS", + "SHA", + "SHA1", + "RSA", + "SSLv3", + "TLSv1", + "TLSv1.2", +}; + +static const CurveTest kCurveTests[] = { + { + "P-256", + { SSL_CURVE_SECP256R1 }, + }, + { + "P-256:P-384:P-521:X25519", + { + SSL_CURVE_SECP256R1, + SSL_CURVE_SECP384R1, + SSL_CURVE_SECP521R1, + SSL_CURVE_X25519, + }, + }, +}; + +static const char *kBadCurvesLists[] = { + "", + ":", + "::", + "P-256::X25519", + "RSA:P-256", + "P-256:RSA", + "X25519:P-256:", + ":X25519:P-256", +}; + +static void PrintCipherPreferenceList(ssl_cipher_preference_list_st *list) { + bool in_group = false; + for (size_t i = 0; i < sk_SSL_CIPHER_num(list->ciphers); i++) { + const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(list->ciphers, i); + if (!in_group && list->in_group_flags[i]) { + fprintf(stderr, "\t[\n"); + in_group = true; + } + fprintf(stderr, "\t"); + if (in_group) { + fprintf(stderr, " "); + } + fprintf(stderr, "%s\n", SSL_CIPHER_get_name(cipher)); + if (in_group && !list->in_group_flags[i]) { + fprintf(stderr, "\t]\n"); + in_group = false; + } + } +} + +static bool TestCipherRule(const CipherTest &t) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + if (!ctx) { + return false; + } + + if (!SSL_CTX_set_cipher_list(ctx.get(), t.rule)) { + fprintf(stderr, "Error testing cipher rule '%s'\n", t.rule); + return false; + } + + if (!SSL_CTX_set_strict_cipher_list(ctx.get(), t.rule) != t.strict_fail) { + fprintf(stderr, "Unexpected strict failure result testing cipher rule '%s':" + " expected %d\n", t.rule, t.strict_fail); + return false; + } + + // Compare the two lists. + if (sk_SSL_CIPHER_num(ctx->cipher_list->ciphers) != t.expected.size()) { + fprintf(stderr, "Error: cipher rule '%s' evaluated to:\n", t.rule); + PrintCipherPreferenceList(ctx->cipher_list); + return false; + } + + for (size_t i = 0; i < t.expected.size(); i++) { + const SSL_CIPHER *cipher = + sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i); + if (t.expected[i].id != SSL_CIPHER_get_id(cipher) || + t.expected[i].in_group_flag != ctx->cipher_list->in_group_flags[i]) { + fprintf(stderr, "Error: cipher rule '%s' evaluated to:\n", t.rule); + PrintCipherPreferenceList(ctx->cipher_list); + return false; + } + } + + return true; +} + +static bool TestRuleDoesNotIncludeNull(const char *rule) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(SSLv23_server_method())); + if (!ctx) { + return false; + } + if (!SSL_CTX_set_strict_cipher_list(ctx.get(), rule)) { + fprintf(stderr, "Error: cipher rule '%s' failed\n", rule); + return false; + } + for (size_t i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); i++) { + if (SSL_CIPHER_is_NULL(sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, i))) { + fprintf(stderr, "Error: cipher rule '%s' includes NULL\n",rule); + return false; + } + } + return true; +} + +static bool TestCipherRules() { + for (const CipherTest &test : kCipherTests) { + if (!TestCipherRule(test)) { + return false; + } + } + + for (const char *rule : kBadRules) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(SSLv23_server_method())); + if (!ctx) { + return false; + } + if (SSL_CTX_set_cipher_list(ctx.get(), rule)) { + fprintf(stderr, "Cipher rule '%s' unexpectedly succeeded\n", rule); + return false; + } + ERR_clear_error(); + } + + for (const char *rule : kMustNotIncludeNull) { + if (!TestRuleDoesNotIncludeNull(rule)) { + return false; + } + } + + return true; +} + +static bool TestCurveRule(const CurveTest &t) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + if (!ctx) { + return false; + } + + if (!SSL_CTX_set1_curves_list(ctx.get(), t.rule)) { + fprintf(stderr, "Error testing curves list '%s'\n", t.rule); + return false; + } + + // Compare the two lists. + if (ctx->supported_group_list_len != t.expected.size()) { + fprintf(stderr, "Error testing curves list '%s': length\n", t.rule); + return false; + } + + for (size_t i = 0; i < t.expected.size(); i++) { + if (t.expected[i] != ctx->supported_group_list[i]) { + fprintf(stderr, "Error testing curves list '%s': mismatch\n", t.rule); + return false; + } + } + + return true; +} + +static bool TestCurveRules() { + for (const CurveTest &test : kCurveTests) { + if (!TestCurveRule(test)) { + return false; + } + } + + for (const char *rule : kBadCurvesLists) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(SSLv23_server_method())); + if (!ctx) { + return false; + } + if (SSL_CTX_set1_curves_list(ctx.get(), rule)) { + fprintf(stderr, "Curves list '%s' unexpectedly succeeded\n", rule); + return false; + } + ERR_clear_error(); + } + + return true; +} + +// kOpenSSLSession is a serialized SSL_SESSION. +static const char kOpenSSLSession[] = + "MIIFqgIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ" + "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH" + "IWoJoQYCBFRDO46iBAICASyjggR6MIIEdjCCA16gAwIBAgIIK9dUvsPWSlUwDQYJ" + "KoZIhvcNAQEFBQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMx" + "JTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTQxMDA4" + "MTIwNzU3WhcNMTUwMTA2MDAwMDAwWjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwK" + "Q2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29v" + "Z2xlIEluYzEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEB" + "AQUAA4IBDwAwggEKAoIBAQCcKeLrplAC+Lofy8t/wDwtB6eu72CVp0cJ4V3lknN6" + "huH9ct6FFk70oRIh/VBNBBz900jYy+7111Jm1b8iqOTQ9aT5C7SEhNcQFJvqzH3e" + "MPkb6ZSWGm1yGF7MCQTGQXF20Sk/O16FSjAynU/b3oJmOctcycWYkY0ytS/k3LBu" + "Id45PJaoMqjB0WypqvNeJHC3q5JjCB4RP7Nfx5jjHSrCMhw8lUMW4EaDxjaR9KDh" + "PLgjsk+LDIySRSRDaCQGhEOWLJZVLzLo4N6/UlctCHEllpBUSvEOyFga52qroGjg" + "rf3WOQ925MFwzd6AK+Ich0gDRg8sQfdLH5OuP1cfLfU1AgMBAAGjggFBMIIBPTAd" + "BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdv" + "b2dsZS5jb20waAYIKwYBBQUHAQEEXDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtp" + "Lmdvb2dsZS5jb20vR0lBRzIuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50" + "czEuZ29vZ2xlLmNvbS9vY3NwMB0GA1UdDgQWBBQ7a+CcxsZByOpc+xpYFcIbnUMZ" + "hTAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEv" + "MBcGA1UdIAQQMA4wDAYKKwYBBAHWeQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRw" + "Oi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCa" + "OXCBdoqUy5bxyq+Wrh1zsyyCFim1PH5VU2+yvDSWrgDY8ibRGJmfff3r4Lud5kal" + "dKs9k8YlKD3ITG7P0YT/Rk8hLgfEuLcq5cc0xqmE42xJ+Eo2uzq9rYorc5emMCxf" + "5L0TJOXZqHQpOEcuptZQ4OjdYMfSxk5UzueUhA3ogZKRcRkdB3WeWRp+nYRhx4St" + "o2rt2A0MKmY9165GHUqMK9YaaXHDXqBu7Sefr1uSoAP9gyIJKeihMivsGqJ1TD6Z" + "cc6LMe+dN2P8cZEQHtD1y296ul4Mivqk3jatUVL8/hCwgch9A8O4PGZq9WqBfEWm" + "IyHh1dPtbg1lOXdYCWtjpAIEAKUDAgEUqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36S" + "YTcLEkXqKwOBfF9vE4KX0NxeLwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9B" + "sNHM362zZnY27GpTw+Kwd751CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yE" + "OTDKPNj3+inbMaVigtK4PLyPq+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdA" + "i4gv7Y5oliyntgMBAQA="; + +// kCustomSession is a custom serialized SSL_SESSION generated by +// filling in missing fields from |kOpenSSLSession|. This includes +// providing |peer_sha256|, so |peer| is not serialized. +static const char kCustomSession[] = + "MIIBdgIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ" + "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH" + "IWoJoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29tqAcE" + "BXdvcmxkqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36SYTcLEkXqKwOBfF9vE4KX0Nxe" + "LwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9BsNHM362zZnY27GpTw+Kwd751" + "CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yEOTDKPNj3+inbMaVigtK4PLyP" + "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG" + "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEF"; + +// kBoringSSLSession is a serialized SSL_SESSION generated from bssl client. +static const char kBoringSSLSession[] = + "MIIRwQIBAQICAwMEAsAvBCDdoGxGK26mR+8lM0uq6+k9xYuxPnwAjpcF9n0Yli9R" + "kQQwbyshfWhdi5XQ1++7n2L1qqrcVlmHBPpr6yknT/u4pUrpQB5FZ7vqvNn8MdHf" + "9rWgoQYCBFXgs7uiBAICHCCjggR6MIIEdjCCA16gAwIBAgIIf+yfD7Y6UicwDQYJ" + "KoZIhvcNAQELBQAwSTELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMx" + "JTAjBgNVBAMTHEdvb2dsZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTUwODEy" + "MTQ1MzE1WhcNMTUxMTEwMDAwMDAwWjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwK" + "Q2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29v" + "Z2xlIEluYzEXMBUGA1UEAwwOd3d3Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEB" + "AQUAA4IBDwAwggEKAoIBAQC0MeG5YGQ0t+IeJeoneP/PrhEaieibeKYkbKVLNZpo" + "PLuBinvhkXZo3DC133NpCBpy6ZktBwamqyixAyuk/NU6OjgXqwwxfQ7di1AInLIU" + "792c7hFyNXSUCG7At8Ifi3YwBX9Ba6u/1d6rWTGZJrdCq3QU11RkKYyTq2KT5mce" + "Tv9iGKqSkSTlp8puy/9SZ/3DbU3U+BuqCFqeSlz7zjwFmk35acdCilpJlVDDN5C/" + "RCh8/UKc8PaL+cxlt531qoTENvYrflBno14YEZlCBZsPiFeUSILpKEj3Ccwhy0eL" + "EucWQ72YZU8mUzXBoXGn0zA0crFl5ci/2sTBBGZsylNBAgMBAAGjggFBMIIBPTAd" + "BgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdv" + "b2dsZS5jb20waAYIKwYBBQUHAQEEXDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtp" + "Lmdvb2dsZS5jb20vR0lBRzIuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50" + "czEuZ29vZ2xlLmNvbS9vY3NwMB0GA1UdDgQWBBS/bzHxcE73Q4j3slC4BLbMtLjG" + "GjAMBgNVHRMBAf8EAjAAMB8GA1UdIwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEv" + "MBcGA1UdIAQQMA4wDAYKKwYBBAHWeQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRw" + "Oi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQAb" + "qdWPZEHk0X7iKPCTHL6S3w6q1eR67goxZGFSM1lk1hjwyu7XcLJuvALVV9uY3ovE" + "kQZSHwT+pyOPWQhsSjO+1GyjvCvK/CAwiUmBX+bQRGaqHsRcio7xSbdVcajQ3bXd" + "X+s0WdbOpn6MStKAiBVloPlSxEI8pxY6x/BBCnTIk/+DMB17uZlOjG3vbAnkDkP+" + "n0OTucD9sHV7EVj9XUxi51nOfNBCN/s7lpUjDS/NJ4k3iwOtbCPswiot8vLO779a" + "f07vR03r349Iz/KTzk95rlFtX0IU+KYNxFNsanIXZ+C9FYGRXkwhHcvFb4qMUB1y" + "TTlM80jBMOwyjZXmjRAhpAIEAKUDAgEUqQUCAwGJwKqBpwSBpOgebbmn9NRUtMWH" + "+eJpqA5JLMFSMCChOsvKey3toBaCNGU7HfAEiiXNuuAdCBoK262BjQc2YYfqFzqH" + "zuppopXCvhohx7j/tnCNZIMgLYt/O9SXK2RYI5z8FhCCHvB4CbD5G0LGl5EFP27s" + "Jb6S3aTTYPkQe8yZSlxevg6NDwmTogLO9F7UUkaYmVcMQhzssEE2ZRYNwSOU6KjE" + "0Yj+8fAiBtbQriIEIN2L8ZlpaVrdN5KFNdvcmOxJu81P8q53X55xQyGTnGWwsgMC" + "ARezggvvMIIEdjCCA16gAwIBAgIIf+yfD7Y6UicwDQYJKoZIhvcNAQELBQAwSTEL" + "MAkGA1UEBhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2ds" + "ZSBJbnRlcm5ldCBBdXRob3JpdHkgRzIwHhcNMTUwODEyMTQ1MzE1WhcNMTUxMTEw" + "MDAwMDAwWjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQG" + "A1UEBwwNTW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEXMBUGA1UE" + "AwwOd3d3Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" + "AQC0MeG5YGQ0t+IeJeoneP/PrhEaieibeKYkbKVLNZpoPLuBinvhkXZo3DC133Np" + "CBpy6ZktBwamqyixAyuk/NU6OjgXqwwxfQ7di1AInLIU792c7hFyNXSUCG7At8If" + "i3YwBX9Ba6u/1d6rWTGZJrdCq3QU11RkKYyTq2KT5mceTv9iGKqSkSTlp8puy/9S" + "Z/3DbU3U+BuqCFqeSlz7zjwFmk35acdCilpJlVDDN5C/RCh8/UKc8PaL+cxlt531" + "qoTENvYrflBno14YEZlCBZsPiFeUSILpKEj3Ccwhy0eLEucWQ72YZU8mUzXBoXGn" + "0zA0crFl5ci/2sTBBGZsylNBAgMBAAGjggFBMIIBPTAdBgNVHSUEFjAUBggrBgEF" + "BQcDAQYIKwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20waAYIKwYB" + "BQUHAQEEXDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lB" + "RzIuY3J0MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50czEuZ29vZ2xlLmNvbS9v" + "Y3NwMB0GA1UdDgQWBBS/bzHxcE73Q4j3slC4BLbMtLjGGjAMBgNVHRMBAf8EAjAA" + "MB8GA1UdIwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEvMBcGA1UdIAQQMA4wDAYK" + "KwYBBAHWeQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vcGtpLmdvb2dsZS5j" + "b20vR0lBRzIuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQAbqdWPZEHk0X7iKPCTHL6S" + "3w6q1eR67goxZGFSM1lk1hjwyu7XcLJuvALVV9uY3ovEkQZSHwT+pyOPWQhsSjO+" + "1GyjvCvK/CAwiUmBX+bQRGaqHsRcio7xSbdVcajQ3bXdX+s0WdbOpn6MStKAiBVl" + "oPlSxEI8pxY6x/BBCnTIk/+DMB17uZlOjG3vbAnkDkP+n0OTucD9sHV7EVj9XUxi" + "51nOfNBCN/s7lpUjDS/NJ4k3iwOtbCPswiot8vLO779af07vR03r349Iz/KTzk95" + "rlFtX0IU+KYNxFNsanIXZ+C9FYGRXkwhHcvFb4qMUB1yTTlM80jBMOwyjZXmjRAh" + "MIID8DCCAtigAwIBAgIDAjqDMA0GCSqGSIb3DQEBCwUAMEIxCzAJBgNVBAYTAlVT" + "MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i" + "YWwgQ0EwHhcNMTMwNDA1MTUxNTU2WhcNMTYxMjMxMjM1OTU5WjBJMQswCQYDVQQG" + "EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy" + "bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB" + "AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP" + "VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv" + "h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE" + "ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ" + "EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC" + "DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB5zCB5DAfBgNVHSMEGDAWgBTAephojYn7" + "qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wDgYD" + "VR0PAQH/BAQDAgEGMC4GCCsGAQUFBwEBBCIwIDAeBggrBgEFBQcwAYYSaHR0cDov" + "L2cuc3ltY2QuY29tMBIGA1UdEwEB/wQIMAYBAf8CAQAwNQYDVR0fBC4wLDAqoCig" + "JoYkaHR0cDovL2cuc3ltY2IuY29tL2NybHMvZ3RnbG9iYWwuY3JsMBcGA1UdIAQQ" + "MA4wDAYKKwYBBAHWeQIFATANBgkqhkiG9w0BAQsFAAOCAQEAqvqpIM1qZ4PtXtR+" + "3h3Ef+AlBgDFJPupyC1tft6dgmUsgWM0Zj7pUsIItMsv91+ZOmqcUHqFBYx90SpI" + "hNMJbHzCzTWf84LuUt5oX+QAihcglvcpjZpNy6jehsgNb1aHA30DP9z6eX0hGfnI" + "Oi9RdozHQZJxjyXON/hKTAAj78Q1EK7gI4BzfE00LshukNYQHpmEcxpw8u1VDu4X" + "Bupn7jLrLN1nBz/2i8Jw3lsA5rsb0zYaImxssDVCbJAJPZPpZAkiDoUGn8JzIdPm" + "X4DkjYUiOnMDsWCOrmji9D6X52ASCWg23jrW4kOVWzeBkoEfu43XrVJkFleW2V40" + "fsg12DCCA30wggLmoAMCAQICAxK75jANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG" + "EwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUg" + "Q2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTAyMDUyMTA0MDAwMFoXDTE4MDgyMTA0" + "MDAwMFowQjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xGzAZ" + "BgNVBAMTEkdlb1RydXN0IEdsb2JhbCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP" + "ADCCAQoCggEBANrMGGMw/fQXIxpWflvfPGw45HG3eJHUvKHYTPioQ7YD6U0hBwiI" + "2lgvZjkpvQV4i5046AW3an5xpObEYKaw74DkiSgPniXW7YPzraaRx5jJQhg1FJ2t" + "mEaSLk/K8YdDwRaVVy1Q74ktgHpXrfLuX2vSAI25FPgUFTXZwEaje3LIkb/JVSvN" + "0Jc+nCZkzN/Ogxlxyk7m1NV7qRnNVd7I7NJeOFPlXE+MLf5QIzb8ZubLjqQ5GQC3" + "lQI5kQsO/jgu0R0FmvZNPm8PBx2vLB6PYDni+jZTEznUXiYr2z2oFL0y6xgDKFIE" + "ceWrMz3hOLsHNoRinHnqFjD0X8Ar6HFr5PkCAwEAAaOB8DCB7TAfBgNVHSMEGDAW" + "gBRI5mj5K9KylddH2CMgEE8zmJCf1DAdBgNVHQ4EFgQUwHqYaI2J+6sFZAwRfap9" + "ZbjKzE4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMw" + "MTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9zZWN1cmVjYS5j" + "cmwwTgYDVR0gBEcwRTBDBgRVHSAAMDswOQYIKwYBBQUHAgEWLWh0dHBzOi8vd3d3" + "Lmdlb3RydXN0LmNvbS9yZXNvdXJjZXMvcmVwb3NpdG9yeTANBgkqhkiG9w0BAQUF" + "AAOBgQB24RJuTksWEoYwBrKBCM/wCMfHcX5m7sLt1Dsf//DwyE7WQziwuTB9GNBV" + "g6JqyzYRnOhIZqNtf7gT1Ef+i1pcc/yu2RsyGTirlzQUqpbS66McFAhJtrvlke+D" + "NusdVm/K2rxzY5Dkf3s+Iss9B+1fOHSc4wNQTqGvmO5h8oQ/Eg=="; + +// kBadSessionExtraField is a custom serialized SSL_SESSION generated by replacing +// the final (optional) element of |kCustomSession| with tag number 30. +static const char kBadSessionExtraField[] = + "MIIBdgIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ" + "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH" + "IWoJoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29tqAcE" + "BXdvcmxkqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36SYTcLEkXqKwOBfF9vE4KX0Nxe" + "LwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9BsNHM362zZnY27GpTw+Kwd751" + "CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yEOTDKPNj3+inbMaVigtK4PLyP" + "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG" + "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBL4DBAEF"; + +// kBadSessionVersion is a custom serialized SSL_SESSION generated by replacing +// the version of |kCustomSession| with 2. +static const char kBadSessionVersion[] = + "MIIBdgIBAgICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ" + "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH" + "IWoJoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29tqAcE" + "BXdvcmxkqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36SYTcLEkXqKwOBfF9vE4KX0Nxe" + "LwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9BsNHM362zZnY27GpTw+Kwd751" + "CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yEOTDKPNj3+inbMaVigtK4PLyP" + "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG" + "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEF"; + +// kBadSessionTrailingData is a custom serialized SSL_SESSION with trailing data +// appended. +static const char kBadSessionTrailingData[] = + "MIIBdgIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ" + "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH" + "IWoJoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29tqAcE" + "BXdvcmxkqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36SYTcLEkXqKwOBfF9vE4KX0Nxe" + "LwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9BsNHM362zZnY27GpTw+Kwd751" + "CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yEOTDKPNj3+inbMaVigtK4PLyP" + "q+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5oliynrSIEIAYGBgYG" + "BgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQBBLADBAEFAAAA"; + +static bool DecodeBase64(std::vector<uint8_t> *out, const char *in) { + size_t len; + if (!EVP_DecodedLength(&len, strlen(in))) { + fprintf(stderr, "EVP_DecodedLength failed\n"); + return false; + } + + out->resize(len); + if (!EVP_DecodeBase64(out->data(), &len, len, (const uint8_t *)in, + strlen(in))) { + fprintf(stderr, "EVP_DecodeBase64 failed\n"); + return false; + } + out->resize(len); + return true; +} + +static bool TestSSL_SESSIONEncoding(const char *input_b64) { + const uint8_t *cptr; + uint8_t *ptr; + + // Decode the input. + std::vector<uint8_t> input; + if (!DecodeBase64(&input, input_b64)) { + return false; + } + + // Verify the SSL_SESSION decodes. + bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method())); + if (!ssl_ctx) { + return false; + } + bssl::UniquePtr<SSL_SESSION> session( + SSL_SESSION_from_bytes(input.data(), input.size(), ssl_ctx.get())); + if (!session) { + fprintf(stderr, "SSL_SESSION_from_bytes failed\n"); + return false; + } + + // Verify the SSL_SESSION encoding round-trips. + size_t encoded_len; + bssl::UniquePtr<uint8_t> encoded; + uint8_t *encoded_raw; + if (!SSL_SESSION_to_bytes(session.get(), &encoded_raw, &encoded_len)) { + fprintf(stderr, "SSL_SESSION_to_bytes failed\n"); + return false; + } + encoded.reset(encoded_raw); + if (encoded_len != input.size() || + OPENSSL_memcmp(input.data(), encoded.get(), input.size()) != 0) { + fprintf(stderr, "SSL_SESSION_to_bytes did not round-trip\n"); + hexdump(stderr, "Before: ", input.data(), input.size()); + hexdump(stderr, "After: ", encoded_raw, encoded_len); + return false; + } + + // Verify the SSL_SESSION also decodes with the legacy API. + cptr = input.data(); + session.reset(d2i_SSL_SESSION(NULL, &cptr, input.size())); + if (!session || cptr != input.data() + input.size()) { + fprintf(stderr, "d2i_SSL_SESSION failed\n"); + return false; + } + + // Verify the SSL_SESSION encoding round-trips via the legacy API. + int len = i2d_SSL_SESSION(session.get(), NULL); + if (len < 0 || (size_t)len != input.size()) { + fprintf(stderr, "i2d_SSL_SESSION(NULL) returned invalid length\n"); + return false; + } + + encoded.reset((uint8_t *)OPENSSL_malloc(input.size())); + if (!encoded) { + fprintf(stderr, "malloc failed\n"); + return false; + } + + ptr = encoded.get(); + len = i2d_SSL_SESSION(session.get(), &ptr); + if (len < 0 || (size_t)len != input.size()) { + fprintf(stderr, "i2d_SSL_SESSION returned invalid length\n"); + return false; + } + if (ptr != encoded.get() + input.size()) { + fprintf(stderr, "i2d_SSL_SESSION did not advance ptr correctly\n"); + return false; + } + if (OPENSSL_memcmp(input.data(), encoded.get(), input.size()) != 0) { + fprintf(stderr, "i2d_SSL_SESSION did not round-trip\n"); + return false; + } + + return true; +} + +static bool TestBadSSL_SESSIONEncoding(const char *input_b64) { + std::vector<uint8_t> input; + if (!DecodeBase64(&input, input_b64)) { + return false; + } + + // Verify that the SSL_SESSION fails to decode. + bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method())); + if (!ssl_ctx) { + return false; + } + bssl::UniquePtr<SSL_SESSION> session( + SSL_SESSION_from_bytes(input.data(), input.size(), ssl_ctx.get())); + if (session) { + fprintf(stderr, "SSL_SESSION_from_bytes unexpectedly succeeded\n"); + return false; + } + ERR_clear_error(); + return true; +} + +static bool TestDefaultVersion(uint16_t min_version, uint16_t max_version, + const SSL_METHOD *(*method)(void)) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method())); + if (!ctx) { + return false; + } + if (ctx->min_version != min_version || ctx->max_version != max_version) { + fprintf(stderr, "Got min %04x, max %04x; wanted min %04x, max %04x\n", + ctx->min_version, ctx->max_version, min_version, max_version); + return false; + } + return true; +} + +static bool CipherGetRFCName(std::string *out, uint16_t value) { + const SSL_CIPHER *cipher = SSL_get_cipher_by_value(value); + if (cipher == NULL) { + return false; + } + bssl::UniquePtr<char> rfc_name(SSL_CIPHER_get_rfc_name(cipher)); + if (!rfc_name) { + return false; + } + out->assign(rfc_name.get()); + return true; +} + +typedef struct { + int id; + const char *rfc_name; +} CIPHER_RFC_NAME_TEST; + +static const CIPHER_RFC_NAME_TEST kCipherRFCNameTests[] = { + {SSL3_CK_RSA_DES_192_CBC3_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, + {TLS1_CK_RSA_WITH_AES_128_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"}, + {TLS1_CK_DHE_RSA_WITH_AES_256_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"}, + {TLS1_CK_DHE_RSA_WITH_AES_256_SHA256, + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"}, + {TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"}, + {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"}, + {TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}, + {TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"}, + {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"}, + {TLS1_CK_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384"}, + {TLS1_CK_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256"}, + {TLS1_CK_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256"}, +}; + +static bool TestCipherGetRFCName(void) { + for (size_t i = 0; + i < OPENSSL_ARRAY_SIZE(kCipherRFCNameTests); i++) { + const CIPHER_RFC_NAME_TEST *test = &kCipherRFCNameTests[i]; + std::string rfc_name; + if (!CipherGetRFCName(&rfc_name, test->id & 0xffff)) { + fprintf(stderr, "SSL_CIPHER_get_rfc_name failed\n"); + return false; + } + if (rfc_name != test->rfc_name) { + fprintf(stderr, "SSL_CIPHER_get_rfc_name: got '%s', wanted '%s'\n", + rfc_name.c_str(), test->rfc_name); + return false; + } + } + return true; +} + +// CreateSessionWithTicket returns a sample |SSL_SESSION| with the specified +// version and ticket length or nullptr on failure. +static bssl::UniquePtr<SSL_SESSION> CreateSessionWithTicket(uint16_t version, + size_t ticket_len) { + std::vector<uint8_t> der; + if (!DecodeBase64(&der, kOpenSSLSession)) { + return nullptr; + } + + bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method())); + if (!ssl_ctx) { + return nullptr; + } + bssl::UniquePtr<SSL_SESSION> session( + SSL_SESSION_from_bytes(der.data(), der.size(), ssl_ctx.get())); + if (!session) { + return nullptr; + } + + session->ssl_version = version; + + // Swap out the ticket for a garbage one. + OPENSSL_free(session->tlsext_tick); + session->tlsext_tick = reinterpret_cast<uint8_t*>(OPENSSL_malloc(ticket_len)); + if (session->tlsext_tick == nullptr) { + return nullptr; + } + OPENSSL_memset(session->tlsext_tick, 'a', ticket_len); + session->tlsext_ticklen = ticket_len; + + // Fix up the timeout. +#if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) + session->time = 1234; +#else + session->time = time(NULL); +#endif + return session; +} + +static bool GetClientHello(SSL *ssl, std::vector<uint8_t> *out) { + bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem())); + if (!bio) { + return false; + } + // Do not configure a reading BIO, but record what's written to a memory BIO. + BIO_up_ref(bio.get()); + SSL_set_bio(ssl, nullptr /* rbio */, bio.get()); + int ret = SSL_connect(ssl); + if (ret > 0) { + // SSL_connect should fail without a BIO to write to. + return false; + } + ERR_clear_error(); + + const uint8_t *client_hello; + size_t client_hello_len; + if (!BIO_mem_contents(bio.get(), &client_hello, &client_hello_len)) { + return false; + } + *out = std::vector<uint8_t>(client_hello, client_hello + client_hello_len); + return true; +} + +// GetClientHelloLen creates a client SSL connection with the specified version +// and ticket length. It returns the length of the ClientHello, not including +// the record header, on success and zero on error. +static size_t GetClientHelloLen(uint16_t max_version, uint16_t session_version, + size_t ticket_len) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + bssl::UniquePtr<SSL_SESSION> session = + CreateSessionWithTicket(session_version, ticket_len); + if (!ctx || !session) { + return 0; + } + + // Set a one-element cipher list so the baseline ClientHello is unpadded. + bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); + if (!ssl || !SSL_set_session(ssl.get(), session.get()) || + !SSL_set_strict_cipher_list(ssl.get(), "ECDHE-RSA-AES128-GCM-SHA256") || + !SSL_set_max_proto_version(ssl.get(), max_version)) { + return 0; + } + + std::vector<uint8_t> client_hello; + if (!GetClientHello(ssl.get(), &client_hello) || + client_hello.size() <= SSL3_RT_HEADER_LENGTH) { + return 0; + } + + return client_hello.size() - SSL3_RT_HEADER_LENGTH; +} + +struct PaddingTest { + size_t input_len, padded_len; +}; + +static const PaddingTest kPaddingTests[] = { + // ClientHellos of length below 0x100 do not require padding. + {0xfe, 0xfe}, + {0xff, 0xff}, + // ClientHellos of length 0x100 through 0x1fb are padded up to 0x200. + {0x100, 0x200}, + {0x123, 0x200}, + {0x1fb, 0x200}, + // ClientHellos of length 0x1fc through 0x1ff get padded beyond 0x200. The + // padding extension takes a minimum of four bytes plus one required content + // byte. (To work around yet more server bugs, we avoid empty final + // extensions.) + {0x1fc, 0x201}, + {0x1fd, 0x202}, + {0x1fe, 0x203}, + {0x1ff, 0x204}, + // Finally, larger ClientHellos need no padding. + {0x200, 0x200}, + {0x201, 0x201}, +}; + +static bool TestPaddingExtension(uint16_t max_version, + uint16_t session_version) { + // Sample a baseline length. + size_t base_len = GetClientHelloLen(max_version, session_version, 1); + if (base_len == 0) { + return false; + } + + for (const PaddingTest &test : kPaddingTests) { + if (base_len > test.input_len) { + fprintf(stderr, + "Baseline ClientHello too long (max_version = %04x, " + "session_version = %04x).\n", + max_version, session_version); + return false; + } + + size_t padded_len = GetClientHelloLen(max_version, session_version, + 1 + test.input_len - base_len); + if (padded_len != test.padded_len) { + fprintf(stderr, + "%u-byte ClientHello padded to %u bytes, not %u (max_version = " + "%04x, session_version = %04x).\n", + static_cast<unsigned>(test.input_len), + static_cast<unsigned>(padded_len), + static_cast<unsigned>(test.padded_len), max_version, + session_version); + return false; + } + } + + return true; +} + +// Test that |SSL_get_client_CA_list| echoes back the configured parameter even +// before configuring as a server. +TEST(SSLTest, ClientCAList) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + + bssl::UniquePtr<X509_NAME> name(X509_NAME_new()); + ASSERT_TRUE(name); + + bssl::UniquePtr<X509_NAME> name_dup(X509_NAME_dup(name.get())); + ASSERT_TRUE(name_dup); + + bssl::UniquePtr<STACK_OF(X509_NAME)> stack(sk_X509_NAME_new_null()); + ASSERT_TRUE(stack); + + ASSERT_TRUE(sk_X509_NAME_push(stack.get(), name_dup.get())); + name_dup.release(); + + // |SSL_set_client_CA_list| takes ownership. + SSL_set_client_CA_list(ssl.get(), stack.release()); + + STACK_OF(X509_NAME) *result = SSL_get_client_CA_list(ssl.get()); + ASSERT_TRUE(result); + ASSERT_EQ(1u, sk_X509_NAME_num(result)); + EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(result, 0), name.get())); +} + +static void AppendSession(SSL_SESSION *session, void *arg) { + std::vector<SSL_SESSION*> *out = + reinterpret_cast<std::vector<SSL_SESSION*>*>(arg); + out->push_back(session); +} + +// ExpectCache returns true if |ctx|'s session cache consists of |expected|, in +// order. +static bool ExpectCache(SSL_CTX *ctx, + const std::vector<SSL_SESSION*> &expected) { + // Check the linked list. + SSL_SESSION *ptr = ctx->session_cache_head; + for (SSL_SESSION *session : expected) { + if (ptr != session) { + return false; + } + // TODO(davidben): This is an absurd way to denote the end of the list. + if (ptr->next == + reinterpret_cast<SSL_SESSION *>(&ctx->session_cache_tail)) { + ptr = nullptr; + } else { + ptr = ptr->next; + } + } + if (ptr != nullptr) { + return false; + } + + // Check the hash table. + std::vector<SSL_SESSION*> actual, expected_copy; + lh_SSL_SESSION_doall_arg(SSL_CTX_sessions(ctx), AppendSession, &actual); + expected_copy = expected; + + std::sort(actual.begin(), actual.end()); + std::sort(expected_copy.begin(), expected_copy.end()); + + return actual == expected_copy; +} + +static bssl::UniquePtr<SSL_SESSION> CreateTestSession(uint32_t number) { + bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method())); + if (!ssl_ctx) { + return nullptr; + } + bssl::UniquePtr<SSL_SESSION> ret(SSL_SESSION_new(ssl_ctx.get())); + if (!ret) { + return nullptr; + } + + ret->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; + OPENSSL_memset(ret->session_id, 0, ret->session_id_length); + OPENSSL_memcpy(ret->session_id, &number, sizeof(number)); + return ret; +} + +// Test that the internal session cache behaves as expected. +static bool TestInternalSessionCache() { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + if (!ctx) { + return false; + } + + // Prepare 10 test sessions. + std::vector<bssl::UniquePtr<SSL_SESSION>> sessions; + for (int i = 0; i < 10; i++) { + bssl::UniquePtr<SSL_SESSION> session = CreateTestSession(i); + if (!session) { + return false; + } + sessions.push_back(std::move(session)); + } + + SSL_CTX_sess_set_cache_size(ctx.get(), 5); + + // Insert all the test sessions. + for (const auto &session : sessions) { + if (!SSL_CTX_add_session(ctx.get(), session.get())) { + return false; + } + } + + // Only the last five should be in the list. + std::vector<SSL_SESSION*> expected = { + sessions[9].get(), + sessions[8].get(), + sessions[7].get(), + sessions[6].get(), + sessions[5].get(), + }; + if (!ExpectCache(ctx.get(), expected)) { + return false; + } + + // Inserting an element already in the cache should fail. + if (SSL_CTX_add_session(ctx.get(), sessions[7].get()) || + !ExpectCache(ctx.get(), expected)) { + return false; + } + + // Although collisions should be impossible (256-bit session IDs), the cache + // must handle them gracefully. + bssl::UniquePtr<SSL_SESSION> collision(CreateTestSession(7)); + if (!collision || !SSL_CTX_add_session(ctx.get(), collision.get())) { + return false; + } + expected = { + collision.get(), + sessions[9].get(), + sessions[8].get(), + sessions[6].get(), + sessions[5].get(), + }; + if (!ExpectCache(ctx.get(), expected)) { + return false; + } + + // Removing sessions behaves correctly. + if (!SSL_CTX_remove_session(ctx.get(), sessions[6].get())) { + return false; + } + expected = { + collision.get(), + sessions[9].get(), + sessions[8].get(), + sessions[5].get(), + }; + if (!ExpectCache(ctx.get(), expected)) { + return false; + } + + // Removing sessions requires an exact match. + if (SSL_CTX_remove_session(ctx.get(), sessions[0].get()) || + SSL_CTX_remove_session(ctx.get(), sessions[7].get()) || + !ExpectCache(ctx.get(), expected)) { + return false; + } + + return true; +} + +static uint16_t EpochFromSequence(uint64_t seq) { + return static_cast<uint16_t>(seq >> 48); +} + +static bssl::UniquePtr<X509> GetTestCertificate() { + static const char kCertPEM[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICWDCCAcGgAwIBAgIJAPuwTC6rEJsMMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV\n" + "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" + "aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDIzMjA1MDQwWhcNMTcwNDIyMjA1MDQwWjBF\n" + "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" + "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB\n" + "gQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92kWdGMdAQhLci\n" + "HnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiFKKAnHmUcrgfV\n" + "W28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQABo1AwTjAdBgNV\n" + "HQ4EFgQUi3XVrMsIvg4fZbf6Vr5sp3Xaha8wHwYDVR0jBBgwFoAUi3XVrMsIvg4f\n" + "Zbf6Vr5sp3Xaha8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQA76Hht\n" + "ldY9avcTGSwbwoiuIqv0jTL1fHFnzy3RHMLDh+Lpvolc5DSrSJHCP5WuK0eeJXhr\n" + "T5oQpHL9z/cCDLAKCKRa4uV0fhEdOWBqyR9p8y5jJtye72t6CuFUV5iqcpF4BH4f\n" + "j2VNHwsSrJwkD4QUGlUtH7vwnQmyCFxZMmWAJg==\n" + "-----END CERTIFICATE-----\n"; + bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM))); + return bssl::UniquePtr<X509>( + PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); +} + +static bssl::UniquePtr<EVP_PKEY> GetTestKey() { + static const char kKeyPEM[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIICXgIBAAKBgQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92\n" + "kWdGMdAQhLciHnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiF\n" + "KKAnHmUcrgfVW28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQAB\n" + "AoGBAIBy09Fd4DOq/Ijp8HeKuCMKTHqTW1xGHshLQ6jwVV2vWZIn9aIgmDsvkjCe\n" + "i6ssZvnbjVcwzSoByhjN8ZCf/i15HECWDFFh6gt0P5z0MnChwzZmvatV/FXCT0j+\n" + "WmGNB/gkehKjGXLLcjTb6dRYVJSCZhVuOLLcbWIV10gggJQBAkEA8S8sGe4ezyyZ\n" + "m4e9r95g6s43kPqtj5rewTsUxt+2n4eVodD+ZUlCULWVNAFLkYRTBCASlSrm9Xhj\n" + "QpmWAHJUkQJBAOVzQdFUaewLtdOJoPCtpYoY1zd22eae8TQEmpGOR11L6kbxLQsk\n" + "aMly/DOnOaa82tqAGTdqDEZgSNmCeKKknmECQAvpnY8GUOVAubGR6c+W90iBuQLj\n" + "LtFp/9ihd2w/PoDwrHZaoUYVcT4VSfJQog/k7kjE4MYXYWL8eEKg3WTWQNECQQDk\n" + "104Wi91Umd1PzF0ijd2jXOERJU1wEKe6XLkYYNHWQAe5l4J4MWj9OdxFXAxIuuR/\n" + "tfDwbqkta4xcux67//khAkEAvvRXLHTaa6VFzTaiiO8SaFsHV3lQyXOtMrBpB5jd\n" + "moZWgjHvB2W9Ckn7sDqsPB+U2tyX0joDdQEyuiMECDY8oQ==\n" + "-----END RSA PRIVATE KEY-----\n"; + bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM))); + return bssl::UniquePtr<EVP_PKEY>( + PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); +} + +static bssl::UniquePtr<X509> GetECDSATestCertificate() { + static const char kCertPEM[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIBzzCCAXagAwIBAgIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC\n" + "QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp\n" + "dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ\n" + "BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l\n" + "dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni\n" + "v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa\n" + "HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw\n" + "HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ\n" + "BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E\n" + "BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=\n" + "-----END CERTIFICATE-----\n"; + bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM))); + return bssl::UniquePtr<X509>(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); +} + +static bssl::UniquePtr<EVP_PKEY> GetECDSATestKey() { + static const char kKeyPEM[] = + "-----BEGIN PRIVATE KEY-----\n" + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBw8IcnrUoEqc3VnJ\n" + "TYlodwi1b8ldMHcO6NHJzgqLtGqhRANCAATmK2niv2Wfl74vHg2UikzVl2u3qR4N\n" + "Rvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLB\n" + "-----END PRIVATE KEY-----\n"; + bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM))); + return bssl::UniquePtr<EVP_PKEY>( + PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); +} + +static bssl::UniquePtr<CRYPTO_BUFFER> BufferFromPEM(const char *pem) { + bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem))); + char *name, *header; + uint8_t *data; + long data_len; + if (!PEM_read_bio(bio.get(), &name, &header, &data, + &data_len)) { + return nullptr; + } + OPENSSL_free(name); + OPENSSL_free(header); + + auto ret = bssl::UniquePtr<CRYPTO_BUFFER>( + CRYPTO_BUFFER_new(data, data_len, nullptr)); + OPENSSL_free(data); + return ret; +} + +static bssl::UniquePtr<CRYPTO_BUFFER> GetChainTestCertificateBuffer() { + static const char kCertPEM[] = + "-----BEGIN CERTIFICATE-----\n" + "MIIC0jCCAbqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBD\n" + "QTAeFw0xNjAyMjgyMDI3MDNaFw0yNjAyMjUyMDI3MDNaMBgxFjAUBgNVBAMMDUNs\n" + "aWVudCBDZXJ0IEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRvaz8\n" + "CC/cshpCafJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/\n" + "kLRcH89M/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3\n" + "tHb+xs2PSs8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+c\n" + "IDs2rQ+lP7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1\n" + "z7C8jU50Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9V\n" + "iLeXANgZi+Xx9KgfAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI\n" + "KwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBFEVbmYl+2RtNw\n" + "rDftRDF1v2QUbcN2ouSnQDHxeDQdSgasLzT3ui8iYu0Rw2WWcZ0DV5e0ztGPhWq7\n" + "AO0B120aFRMOY+4+bzu9Q2FFkQqc7/fKTvTDzIJI5wrMnFvUfzzvxh3OHWMYSs/w\n" + "giq33hTKeHEq6Jyk3btCny0Ycecyc3yGXH10sizUfiHlhviCkDuESk8mFDwDDzqW\n" + "ZF0IipzFbEDHoIxLlm3GQxpiLoEV4k8KYJp3R5KBLFyxM6UGPz8h72mIPCJp2RuK\n" + "MYgF91UDvVzvnYm6TfseM2+ewKirC00GOrZ7rEcFvtxnKSqYf4ckqfNdSU1Y+RRC\n" + "1ngWZ7Ih\n" + "-----END CERTIFICATE-----\n"; + return BufferFromPEM(kCertPEM); +} + +static bssl::UniquePtr<X509> X509FromBuffer( + bssl::UniquePtr<CRYPTO_BUFFER> buffer) { + if (!buffer) { + return nullptr; + } + const uint8_t *derp = CRYPTO_BUFFER_data(buffer.get()); + return bssl::UniquePtr<X509>( + d2i_X509(NULL, &derp, CRYPTO_BUFFER_len(buffer.get()))); +} + +static bssl::UniquePtr<X509> GetChainTestCertificate() { + return X509FromBuffer(GetChainTestCertificateBuffer()); +} + +static bssl::UniquePtr<CRYPTO_BUFFER> GetChainTestIntermediateBuffer() { + static const char kCertPEM[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICwjCCAaqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJQyBS\n" + "b290IENBMB4XDTE2MDIyODIwMjcwM1oXDTI2MDIyNTIwMjcwM1owDzENMAsGA1UE\n" + "AwwEQiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALsSCYmDip2D\n" + "GkjFxw7ykz26JSjELkl6ArlYjFJ3aT/SCh8qbS4gln7RH8CPBd78oFdfhIKQrwtZ\n" + "3/q21ykD9BAS3qHe2YdcJfm8/kWAy5DvXk6NXU4qX334KofBAEpgdA/igEFq1P1l\n" + "HAuIfZCpMRfT+i5WohVsGi8f/NgpRvVaMONLNfgw57mz1lbtFeBEISmX0kbsuJxF\n" + "Qj/Bwhi5/0HAEXG8e7zN4cEx0yPRvmOATRdVb/8dW2pwOHRJq9R5M0NUkIsTSnL7\n" + "6N/z8hRAHMsV3IudC5Yd7GXW1AGu9a+iKU+Q4xcZCoj0DC99tL4VKujrV1kAeqsM\n" + "cz5/dKzi6+cCAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" + "AQYwDQYJKoZIhvcNAQELBQADggEBAIIeZiEeNhWWQ8Y4D+AGDwqUUeG8NjCbKrXQ\n" + "BlHg5wZ8xftFaiP1Dp/UAezmx2LNazdmuwrYB8lm3FVTyaPDTKEGIPS4wJKHgqH1\n" + "QPDhqNm85ey7TEtI9oYjsNim/Rb+iGkIAMXaxt58SzxbjvP0kMr1JfJIZbic9vye\n" + "NwIspMFIpP3FB8ywyu0T0hWtCQgL4J47nigCHpOu58deP88fS/Nyz/fyGVWOZ76b\n" + "WhWwgM3P3X95fQ3d7oFPR/bVh0YV+Cf861INwplokXgXQ3/TCQ+HNXeAMWn3JLWv\n" + "XFwk8owk9dq/kQGdndGgy3KTEW4ctPX5GNhf3LJ9Q7dLji4ReQ4=\n" + "-----END CERTIFICATE-----\n"; + return BufferFromPEM(kCertPEM); +} + +static bssl::UniquePtr<X509> GetChainTestIntermediate() { + return X509FromBuffer(GetChainTestIntermediateBuffer()); +} + +static bssl::UniquePtr<EVP_PKEY> GetChainTestKey() { + static const char kKeyPEM[] = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRvaz8CC/cshpC\n" + "afJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/kLRcH89M\n" + "/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3tHb+xs2P\n" + "Ss8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+cIDs2rQ+l\n" + "P7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1z7C8jU50\n" + "Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9ViLeXANgZ\n" + "i+Xx9KgfAgMBAAECggEBAK0VjSJzkyPaamcyTVSWjo7GdaBGcK60lk657RjR+lK0\n" + "YJ7pkej4oM2hdsVZFsP8Cs4E33nXLa/0pDsRov/qrp0WQm2skwqGMC1I/bZ0WRPk\n" + "wHaDrBBfESWnJDX/AGpVtlyOjPmgmK6J2usMPihQUDkKdAYrVWJePrMIxt1q6BMe\n" + "iczs3qriMmtY3bUc4UyUwJ5fhDLjshHvfuIpYQyI6EXZM6dZksn9LylXJnigY6QJ\n" + "HxOYO0BDwOsZ8yQ8J8afLk88i0GizEkgE1z3REtQUwgWfxr1WV/ud+T6/ZhSAgH9\n" + "042mQvSFZnIUSEsmCvjhWuAunfxHKCTcAoYISWfzWpkCgYEA7gpf3HHU5Tn+CgUn\n" + "1X5uGpG3DmcMgfeGgs2r2f/IIg/5Ac1dfYILiybL1tN9zbyLCJfcbFpWBc9hJL6f\n" + "CPc5hUiwWFJqBJewxQkC1Ae/HakHbip+IZ+Jr0842O4BAArvixk4Lb7/N2Ct9sTE\n" + "NJO6RtK9lbEZ5uK61DglHy8CS2UCgYEA4ZC1o36kPAMQBggajgnucb2yuUEelk0f\n" + "AEr+GI32MGE+93xMr7rAhBoqLg4AITyIfEnOSQ5HwagnIHonBbv1LV/Gf9ursx8Z\n" + "YOGbvT8zzzC+SU1bkDzdjAYnFQVGIjMtKOBJ3K07++ypwX1fr4QsQ8uKL8WSOWwt\n" + "Z3Bym6XiZzMCgYADnhy+2OwHX85AkLt+PyGlPbmuelpyTzS4IDAQbBa6jcuW/2wA\n" + "UE2km75VUXmD+u2R/9zVuLm99NzhFhSMqlUxdV1YukfqMfP5yp1EY6m/5aW7QuIP\n" + "2MDa7TVL9rIFMiVZ09RKvbBbQxjhuzPQKL6X/PPspnhiTefQ+dl2k9xREQKBgHDS\n" + "fMfGNEeAEKezrfSVqxphE9/tXms3L+ZpnCaT+yu/uEr5dTIAawKoQ6i9f/sf1/Sy\n" + "xedsqR+IB+oKrzIDDWMgoJybN4pkZ8E5lzhVQIjFjKgFdWLzzqyW9z1gYfABQPlN\n" + "FiS20WX0vgP1vcKAjdNrHzc9zyHBpgQzDmAj3NZZAoGBAI8vKCKdH7w3aL5CNkZQ\n" + "2buIeWNA2HZazVwAGG5F2TU/LmXfRKnG6dX5bkU+AkBZh56jNZy//hfFSewJB4Kk\n" + "buB7ERSdaNbO21zXt9FEA3+z0RfMd/Zv2vlIWOSB5nzl/7UKti3sribK6s9ZVLfi\n" + "SxpiPQ8d/hmSGwn4ksrWUsJD\n" + "-----END PRIVATE KEY-----\n"; + bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM))); + return bssl::UniquePtr<EVP_PKEY>( + PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); +} + +static bool CompleteHandshakes(SSL *client, SSL *server) { + // Drive both their handshakes to completion. + for (;;) { + int client_ret = SSL_do_handshake(client); + int client_err = SSL_get_error(client, client_ret); + if (client_err != SSL_ERROR_NONE && + client_err != SSL_ERROR_WANT_READ && + client_err != SSL_ERROR_WANT_WRITE && + client_err != SSL_ERROR_PENDING_TICKET) { + fprintf(stderr, "Client error: %d\n", client_err); + return false; + } + + int server_ret = SSL_do_handshake(server); + int server_err = SSL_get_error(server, server_ret); + if (server_err != SSL_ERROR_NONE && + server_err != SSL_ERROR_WANT_READ && + server_err != SSL_ERROR_WANT_WRITE && + server_err != SSL_ERROR_PENDING_TICKET) { + fprintf(stderr, "Server error: %d\n", server_err); + return false; + } + + if (client_ret == 1 && server_ret == 1) { + break; + } + } + + return true; +} + +static bool ConnectClientAndServer(bssl::UniquePtr<SSL> *out_client, + bssl::UniquePtr<SSL> *out_server, + SSL_CTX *client_ctx, SSL_CTX *server_ctx, + SSL_SESSION *session) { + bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx)); + if (!client || !server) { + return false; + } + SSL_set_connect_state(client.get()); + SSL_set_accept_state(server.get()); + + SSL_set_session(client.get(), session); + + BIO *bio1, *bio2; + if (!BIO_new_bio_pair(&bio1, 0, &bio2, 0)) { + return false; + } + // SSL_set_bio takes ownership. + SSL_set_bio(client.get(), bio1, bio1); + SSL_set_bio(server.get(), bio2, bio2); + + if (!CompleteHandshakes(client.get(), server.get())) { + return false; + } + + *out_client = std::move(client); + *out_server = std::move(server); + return true; +} + +static bool TestSequenceNumber(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method)); + if (!server_ctx || !client_ctx || + !SSL_CTX_set_min_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_min_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) { + return false; + } + + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + if (!cert || !key || !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) { + return false; + } + + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get(), nullptr /* no session */)) { + return false; + } + + // Drain any post-handshake messages to ensure there are no unread records + // on either end. + uint8_t byte = 0; + if (SSL_read(client.get(), &byte, 1) > 0 || + SSL_read(server.get(), &byte, 1) > 0) { + fprintf(stderr, "Received unexpected data.\n"); + return false; + } + + uint64_t client_read_seq = SSL_get_read_sequence(client.get()); + uint64_t client_write_seq = SSL_get_write_sequence(client.get()); + uint64_t server_read_seq = SSL_get_read_sequence(server.get()); + uint64_t server_write_seq = SSL_get_write_sequence(server.get()); + + if (is_dtls) { + // Both client and server must be at epoch 1. + if (EpochFromSequence(client_read_seq) != 1 || + EpochFromSequence(client_write_seq) != 1 || + EpochFromSequence(server_read_seq) != 1 || + EpochFromSequence(server_write_seq) != 1) { + fprintf(stderr, "Bad epochs.\n"); + return false; + } + + // The next record to be written should exceed the largest received. + if (client_write_seq <= server_read_seq || + server_write_seq <= client_read_seq) { + fprintf(stderr, "Inconsistent sequence numbers.\n"); + return false; + } + } else { + // The next record to be written should equal the next to be received. + if (client_write_seq != server_read_seq || + server_write_seq != client_read_seq) { + fprintf(stderr, "Inconsistent sequence numbers.\n"); + return false; + } + } + + // Send a record from client to server. + if (SSL_write(client.get(), &byte, 1) != 1 || + SSL_read(server.get(), &byte, 1) != 1) { + fprintf(stderr, "Could not send byte.\n"); + return false; + } + + // The client write and server read sequence numbers should have + // incremented. + if (client_write_seq + 1 != SSL_get_write_sequence(client.get()) || + server_read_seq + 1 != SSL_get_read_sequence(server.get())) { + fprintf(stderr, "Sequence numbers did not increment.\n"); + return false; + } + + return true; +} + +static bool TestOneSidedShutdown(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + // SSL_shutdown is a no-op in DTLS. + if (is_dtls) { + return true; + } + + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + if (!client_ctx || !server_ctx || !cert || !key || + !SSL_CTX_set_min_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_min_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(client_ctx.get(), version) || + !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())) { + return false; + } + + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get(), nullptr /* no session */)) { + return false; + } + + // Shut down half the connection. SSL_shutdown will return 0 to signal only + // one side has shut down. + if (SSL_shutdown(client.get()) != 0) { + fprintf(stderr, "Could not shutdown.\n"); + return false; + } + + // Reading from the server should consume the EOF. + uint8_t byte; + if (SSL_read(server.get(), &byte, 1) != 0 || + SSL_get_error(server.get(), 0) != SSL_ERROR_ZERO_RETURN) { + fprintf(stderr, "Connection was not shut down cleanly.\n"); + return false; + } + + // However, the server may continue to write data and then shut down the + // connection. + byte = 42; + if (SSL_write(server.get(), &byte, 1) != 1 || + SSL_read(client.get(), &byte, 1) != 1 || + byte != 42) { + fprintf(stderr, "Could not send byte.\n"); + return false; + } + + // The server may then shutdown the connection. + if (SSL_shutdown(server.get()) != 1 || + SSL_shutdown(client.get()) != 1) { + fprintf(stderr, "Could not complete shutdown.\n"); + return false; + } + + return true; +} + +TEST(SSLTest, SessionDuplication) { + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(client_ctx); + ASSERT_TRUE(server_ctx); + + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + ASSERT_TRUE(cert); + ASSERT_TRUE(key); + ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get())); + ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())); + + bssl::UniquePtr<SSL> client, server; + ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get(), + nullptr /* no session */)); + + SSL_SESSION *session0 = SSL_get_session(client.get()); + bssl::UniquePtr<SSL_SESSION> session1( + SSL_SESSION_dup(session0, SSL_SESSION_DUP_ALL)); + ASSERT_TRUE(session1); + + session1->not_resumable = 0; + + uint8_t *s0_bytes, *s1_bytes; + size_t s0_len, s1_len; + + ASSERT_TRUE(SSL_SESSION_to_bytes(session0, &s0_bytes, &s0_len)); + bssl::UniquePtr<uint8_t> free_s0(s0_bytes); + + ASSERT_TRUE(SSL_SESSION_to_bytes(session1.get(), &s1_bytes, &s1_len)); + bssl::UniquePtr<uint8_t> free_s1(s1_bytes); + + EXPECT_EQ(Bytes(s0_bytes, s0_len), Bytes(s1_bytes, s1_len)); +} + +static void ExpectFDs(const SSL *ssl, int rfd, int wfd) { + EXPECT_EQ(rfd, SSL_get_rfd(ssl)); + EXPECT_EQ(wfd, SSL_get_wfd(ssl)); + + // The wrapper BIOs are always equal when fds are equal, even if set + // individually. + if (rfd == wfd) { + EXPECT_EQ(SSL_get_rbio(ssl), SSL_get_wbio(ssl)); + } +} + +TEST(SSLTest, SetFD) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + + // Test setting different read and write FDs. + bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + EXPECT_TRUE(SSL_set_rfd(ssl.get(), 1)); + EXPECT_TRUE(SSL_set_wfd(ssl.get(), 2)); + ExpectFDs(ssl.get(), 1, 2); + + // Test setting the same FD. + ssl.reset(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + EXPECT_TRUE(SSL_set_fd(ssl.get(), 1)); + ExpectFDs(ssl.get(), 1, 1); + + // Test setting the same FD one side at a time. + ssl.reset(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + EXPECT_TRUE(SSL_set_rfd(ssl.get(), 1)); + EXPECT_TRUE(SSL_set_wfd(ssl.get(), 1)); + ExpectFDs(ssl.get(), 1, 1); + + // Test setting the same FD in the other order. + ssl.reset(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + EXPECT_TRUE(SSL_set_wfd(ssl.get(), 1)); + EXPECT_TRUE(SSL_set_rfd(ssl.get(), 1)); + ExpectFDs(ssl.get(), 1, 1); + + // Test changing the read FD partway through. + ssl.reset(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + EXPECT_TRUE(SSL_set_fd(ssl.get(), 1)); + EXPECT_TRUE(SSL_set_rfd(ssl.get(), 2)); + ExpectFDs(ssl.get(), 2, 1); + + // Test changing the write FD partway through. + ssl.reset(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + EXPECT_TRUE(SSL_set_fd(ssl.get(), 1)); + EXPECT_TRUE(SSL_set_wfd(ssl.get(), 2)); + ExpectFDs(ssl.get(), 1, 2); + + // Test a no-op change to the read FD partway through. + ssl.reset(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + EXPECT_TRUE(SSL_set_fd(ssl.get(), 1)); + EXPECT_TRUE(SSL_set_rfd(ssl.get(), 1)); + ExpectFDs(ssl.get(), 1, 1); + + // Test a no-op change to the write FD partway through. + ssl.reset(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + EXPECT_TRUE(SSL_set_fd(ssl.get(), 1)); + EXPECT_TRUE(SSL_set_wfd(ssl.get(), 1)); + ExpectFDs(ssl.get(), 1, 1); + + // ASan builds will implicitly test that the internal |BIO| reference-counting + // is correct. +} + +TEST(SSLTest, SetBIO) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + + bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); + bssl::UniquePtr<BIO> bio1(BIO_new(BIO_s_mem())), bio2(BIO_new(BIO_s_mem())), + bio3(BIO_new(BIO_s_mem())); + ASSERT_TRUE(ssl); + ASSERT_TRUE(bio1); + ASSERT_TRUE(bio2); + ASSERT_TRUE(bio3); + + // SSL_set_bio takes one reference when the parameters are the same. + BIO_up_ref(bio1.get()); + SSL_set_bio(ssl.get(), bio1.get(), bio1.get()); + + // Repeating the call does nothing. + SSL_set_bio(ssl.get(), bio1.get(), bio1.get()); + + // It takes one reference each when the parameters are different. + BIO_up_ref(bio2.get()); + BIO_up_ref(bio3.get()); + SSL_set_bio(ssl.get(), bio2.get(), bio3.get()); + + // Repeating the call does nothing. + SSL_set_bio(ssl.get(), bio2.get(), bio3.get()); + + // It takes one reference when changing only wbio. + BIO_up_ref(bio1.get()); + SSL_set_bio(ssl.get(), bio2.get(), bio1.get()); + + // It takes one reference when changing only rbio and the two are different. + BIO_up_ref(bio3.get()); + SSL_set_bio(ssl.get(), bio3.get(), bio1.get()); + + // If setting wbio to rbio, it takes no additional references. + SSL_set_bio(ssl.get(), bio3.get(), bio3.get()); + + // From there, wbio may be switched to something else. + BIO_up_ref(bio1.get()); + SSL_set_bio(ssl.get(), bio3.get(), bio1.get()); + + // If setting rbio to wbio, it takes no additional references. + SSL_set_bio(ssl.get(), bio1.get(), bio1.get()); + + // From there, rbio may be switched to something else, but, for historical + // reasons, it takes a reference to both parameters. + BIO_up_ref(bio1.get()); + BIO_up_ref(bio2.get()); + SSL_set_bio(ssl.get(), bio2.get(), bio1.get()); + + // ASAN builds will implicitly test that the internal |BIO| reference-counting + // is correct. +} + +static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { return 1; } + +static bool TestGetPeerCertificate(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + if (!cert || !key) { + return false; + } + + // Configure both client and server to accept any certificate. + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method)); + if (!ctx || + !SSL_CTX_use_certificate(ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) || + !SSL_CTX_set_min_proto_version(ctx.get(), version) || + !SSL_CTX_set_max_proto_version(ctx.get(), version)) { + return false; + } + SSL_CTX_set_verify( + ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL); + + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(), + nullptr /* no session */)) { + return false; + } + + // Client and server should both see the leaf certificate. + bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get())); + if (!peer || X509_cmp(cert.get(), peer.get()) != 0) { + fprintf(stderr, "Server peer certificate did not match.\n"); + return false; + } + + peer.reset(SSL_get_peer_certificate(client.get())); + if (!peer || X509_cmp(cert.get(), peer.get()) != 0) { + fprintf(stderr, "Client peer certificate did not match.\n"); + return false; + } + + // However, for historical reasons, the chain includes the leaf on the + // client, but does not on the server. + if (sk_X509_num(SSL_get_peer_cert_chain(client.get())) != 1) { + fprintf(stderr, "Client peer chain was incorrect.\n"); + return false; + } + + if (sk_X509_num(SSL_get_peer_cert_chain(server.get())) != 0) { + fprintf(stderr, "Server peer chain was incorrect.\n"); + return false; + } + + return true; +} + +static bool TestRetainOnlySHA256OfCerts(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + if (!cert || !key) { + return false; + } + + uint8_t *cert_der = NULL; + int cert_der_len = i2d_X509(cert.get(), &cert_der); + if (cert_der_len < 0) { + return false; + } + bssl::UniquePtr<uint8_t> free_cert_der(cert_der); + + uint8_t cert_sha256[SHA256_DIGEST_LENGTH]; + SHA256(cert_der, cert_der_len, cert_sha256); + + // Configure both client and server to accept any certificate, but the + // server must retain only the SHA-256 of the peer. + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method)); + if (!ctx || + !SSL_CTX_use_certificate(ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) || + !SSL_CTX_set_min_proto_version(ctx.get(), version) || + !SSL_CTX_set_max_proto_version(ctx.get(), version)) { + return false; + } + SSL_CTX_set_verify( + ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL); + SSL_CTX_set_retain_only_sha256_of_client_certs(ctx.get(), 1); + + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(), + nullptr /* no session */)) { + return false; + } + + // The peer certificate has been dropped. + bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get())); + if (peer) { + fprintf(stderr, "Peer certificate was retained.\n"); + return false; + } + + SSL_SESSION *session = SSL_get_session(server.get()); + if (!session->peer_sha256_valid) { + fprintf(stderr, "peer_sha256_valid was not set.\n"); + return false; + } + + if (OPENSSL_memcmp(cert_sha256, session->peer_sha256, SHA256_DIGEST_LENGTH) != + 0) { + fprintf(stderr, "peer_sha256 did not match.\n"); + return false; + } + + return true; +} + +static bool ClientHelloMatches(uint16_t version, const uint8_t *expected, + size_t expected_len) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + if (!ctx || + !SSL_CTX_set_max_proto_version(ctx.get(), version) || + // Our default cipher list varies by CPU capabilities, so manually place + // the ChaCha20 ciphers in front. + !SSL_CTX_set_strict_cipher_list(ctx.get(), "CHACHA20:ALL")) { + return false; + } + + bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); + if (!ssl) { + return false; + } + std::vector<uint8_t> client_hello; + if (!GetClientHello(ssl.get(), &client_hello)) { + return false; + } + + // Zero the client_random. + constexpr size_t kRandomOffset = 1 + 2 + 2 + // record header + 1 + 3 + // handshake message header + 2; // client_version + if (client_hello.size() < kRandomOffset + SSL3_RANDOM_SIZE) { + fprintf(stderr, "ClientHello for version %04x too short.\n", version); + return false; + } + OPENSSL_memset(client_hello.data() + kRandomOffset, 0, SSL3_RANDOM_SIZE); + + if (client_hello.size() != expected_len || + OPENSSL_memcmp(client_hello.data(), expected, expected_len) != 0) { + fprintf(stderr, "ClientHello for version %04x did not match:\n", version); + fprintf(stderr, "Got:\n\t"); + for (size_t i = 0; i < client_hello.size(); i++) { + fprintf(stderr, "0x%02x, ", client_hello[i]); + } + fprintf(stderr, "\nWanted:\n\t"); + for (size_t i = 0; i < expected_len; i++) { + fprintf(stderr, "0x%02x, ", expected[i]); + } + fprintf(stderr, "\n"); + return false; + } + + return true; +} + +// Tests that our ClientHellos do not change unexpectedly. +static bool TestClientHello() { + static const uint8_t kSSL3ClientHello[] = { + 0x16, + 0x03, 0x00, + 0x00, 0x3f, + 0x01, + 0x00, 0x00, 0x3b, + 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x14, + 0xc0, 0x09, + 0xc0, 0x13, + 0x00, 0x33, + 0xc0, 0x0a, + 0xc0, 0x14, + 0x00, 0x39, + 0x00, 0x2f, + 0x00, 0x35, + 0x00, 0x0a, + 0x00, 0xff, 0x01, 0x00, + }; + if (!ClientHelloMatches(SSL3_VERSION, kSSL3ClientHello, + sizeof(kSSL3ClientHello))) { + return false; + } + + static const uint8_t kTLS1ClientHello[] = { + 0x16, + 0x03, 0x01, + 0x00, 0x5e, + 0x01, + 0x00, 0x00, 0x5a, + 0x03, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x12, + 0xc0, 0x09, + 0xc0, 0x13, + 0x00, 0x33, + 0xc0, 0x0a, + 0xc0, 0x14, + 0x00, 0x39, + 0x00, 0x2f, + 0x00, 0x35, + 0x00, 0x0a, + 0x01, 0x00, 0x00, 0x1f, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + }; + if (!ClientHelloMatches(TLS1_VERSION, kTLS1ClientHello, + sizeof(kTLS1ClientHello))) { + return false; + } + + static const uint8_t kTLS11ClientHello[] = { + 0x16, + 0x03, 0x01, + 0x00, 0x5e, + 0x01, + 0x00, 0x00, 0x5a, + 0x03, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x12, + 0xc0, 0x09, + 0xc0, 0x13, + 0x00, 0x33, + 0xc0, 0x0a, + 0xc0, 0x14, + 0x00, 0x39, + 0x00, 0x2f, + 0x00, 0x35, + 0x00, 0x0a, + 0x01, 0x00, 0x00, 0x1f, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, + }; + if (!ClientHelloMatches(TLS1_1_VERSION, kTLS11ClientHello, + sizeof(kTLS11ClientHello))) { + return false; + } + + // kTLS12ClientHello assumes RSA-PSS, which is disabled for Android system + // builds. +#if defined(BORINGSSL_ANDROID_SYSTEM) + return true; +#endif + + static const uint8_t kTLS12ClientHello[] = { + 0x16, 0x03, 0x01, 0x00, 0x9a, 0x01, 0x00, 0x00, 0x96, 0x03, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xcc, 0xa9, + 0xcc, 0xa8, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e, 0xc0, 0x2c, 0xc0, 0x30, + 0x00, 0x9f, 0xc0, 0x09, 0xc0, 0x23, 0xc0, 0x13, 0xc0, 0x27, 0x00, 0x33, + 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x24, 0xc0, 0x14, 0xc0, 0x28, 0x00, 0x39, + 0x00, 0x6b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x35, + 0x00, 0x3d, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37, 0xff, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08, + 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x0b, 0x00, + 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, + 0x17, 0x00, 0x18, + }; + if (!ClientHelloMatches(TLS1_2_VERSION, kTLS12ClientHello, + sizeof(kTLS12ClientHello))) { + return false; + } + + // TODO(davidben): Add a change detector for TLS 1.3 once the spec and our + // implementation has settled enough that it won't change. + + return true; +} + +static bssl::UniquePtr<SSL_SESSION> g_last_session; + +static int SaveLastSession(SSL *ssl, SSL_SESSION *session) { + // Save the most recent session. + g_last_session.reset(session); + return 1; +} + +static bssl::UniquePtr<SSL_SESSION> CreateClientSession(SSL_CTX *client_ctx, + SSL_CTX *server_ctx) { + g_last_session = nullptr; + SSL_CTX_sess_set_new_cb(client_ctx, SaveLastSession); + + // Connect client and server to get a session. + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, client_ctx, server_ctx, + nullptr /* no session */)) { + fprintf(stderr, "Failed to connect client and server.\n"); + return nullptr; + } + + // Run the read loop to account for post-handshake tickets in TLS 1.3. + SSL_read(client.get(), nullptr, 0); + + SSL_CTX_sess_set_new_cb(client_ctx, nullptr); + + if (!g_last_session) { + fprintf(stderr, "Client did not receive a session.\n"); + return nullptr; + } + return std::move(g_last_session); +} + +static bool ExpectSessionReused(SSL_CTX *client_ctx, SSL_CTX *server_ctx, + SSL_SESSION *session, + bool reused) { + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, client_ctx, + server_ctx, session)) { + fprintf(stderr, "Failed to connect client and server.\n"); + return false; + } + + if (SSL_session_reused(client.get()) != SSL_session_reused(server.get())) { + fprintf(stderr, "Client and server were inconsistent.\n"); + return false; + } + + bool was_reused = !!SSL_session_reused(client.get()); + if (was_reused != reused) { + fprintf(stderr, "Session was%s reused, but we expected the opposite.\n", + was_reused ? "" : " not"); + return false; + } + + return true; +} + +static bssl::UniquePtr<SSL_SESSION> ExpectSessionRenewed(SSL_CTX *client_ctx, + SSL_CTX *server_ctx, + SSL_SESSION *session) { + g_last_session = nullptr; + SSL_CTX_sess_set_new_cb(client_ctx, SaveLastSession); + + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, client_ctx, + server_ctx, session)) { + fprintf(stderr, "Failed to connect client and server.\n"); + return nullptr; + } + + if (SSL_session_reused(client.get()) != SSL_session_reused(server.get())) { + fprintf(stderr, "Client and server were inconsistent.\n"); + return nullptr; + } + + if (!SSL_session_reused(client.get())) { + fprintf(stderr, "Session was not reused.\n"); + return nullptr; + } + + // Run the read loop to account for post-handshake tickets in TLS 1.3. + SSL_read(client.get(), nullptr, 0); + + SSL_CTX_sess_set_new_cb(client_ctx, nullptr); + + if (!g_last_session) { + fprintf(stderr, "Client did not receive a renewed session.\n"); + return nullptr; + } + return std::move(g_last_session); +} + +static int SwitchSessionIDContextSNI(SSL *ssl, int *out_alert, void *arg) { + static const uint8_t kContext[] = {3}; + + if (!SSL_set_session_id_context(ssl, kContext, sizeof(kContext))) { + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + return SSL_TLSEXT_ERR_OK; +} + +static int SwitchSessionIDContextEarly(const SSL_CLIENT_HELLO *client_hello) { + static const uint8_t kContext[] = {3}; + + if (!SSL_set_session_id_context(client_hello->ssl, kContext, + sizeof(kContext))) { + return -1; + } + + return 1; +} + +static bool TestSessionIDContext(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + if (!cert || !key) { + return false; + } + + static const uint8_t kContext1[] = {1}; + static const uint8_t kContext2[] = {2}; + + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method)); + if (!server_ctx || !client_ctx || + !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) || + !SSL_CTX_set_session_id_context(server_ctx.get(), kContext1, + sizeof(kContext1)) || + !SSL_CTX_set_min_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_min_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) { + return false; + } + + SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH); + SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH); + + bssl::UniquePtr<SSL_SESSION> session = + CreateClientSession(client_ctx.get(), server_ctx.get()); + if (!session) { + fprintf(stderr, "Error getting session.\n"); + return false; + } + + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(), + true /* expect session reused */)) { + fprintf(stderr, "Error resuming session.\n"); + return false; + } + + // Change the session ID context. + if (!SSL_CTX_set_session_id_context(server_ctx.get(), kContext2, + sizeof(kContext2))) { + return false; + } + + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(), + false /* expect session not reused */)) { + fprintf(stderr, "Error connecting with a different context.\n"); + return false; + } + + // Change the session ID context back and install an SNI callback to switch + // it. + if (!SSL_CTX_set_session_id_context(server_ctx.get(), kContext1, + sizeof(kContext1))) { + return false; + } + + SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), + SwitchSessionIDContextSNI); + + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(), + false /* expect session not reused */)) { + fprintf(stderr, "Error connecting with a context switch on SNI callback.\n"); + return false; + } + + // Switch the session ID context with the early callback instead. + SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), nullptr); + SSL_CTX_set_select_certificate_cb(server_ctx.get(), + SwitchSessionIDContextEarly); + + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(), + false /* expect session not reused */)) { + fprintf(stderr, + "Error connecting with a context switch on early callback.\n"); + return false; + } + + return true; +} + +static timeval g_current_time; + +static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) { + *out_clock = g_current_time; +} + +static void FrozenTimeCallback(const SSL *ssl, timeval *out_clock) { + out_clock->tv_sec = 1000; + out_clock->tv_usec = 0; +} + +static int RenewTicketCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv, + EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, + int encrypt) { + static const uint8_t kZeros[16] = {0}; + + if (encrypt) { + OPENSSL_memcpy(key_name, kZeros, sizeof(kZeros)); + RAND_bytes(iv, 16); + } else if (OPENSSL_memcmp(key_name, kZeros, 16) != 0) { + return 0; + } + + if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) || + !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) { + return -1; + } + + // Returning two from the callback in decrypt mode renews the + // session in TLS 1.2 and below. + return encrypt ? 1 : 2; +} + +static bool GetServerTicketTime(long *out, const SSL_SESSION *session) { + if (session->tlsext_ticklen < 16 + 16 + SHA256_DIGEST_LENGTH) { + return false; + } + + const uint8_t *ciphertext = session->tlsext_tick + 16 + 16; + size_t len = session->tlsext_ticklen - 16 - 16 - SHA256_DIGEST_LENGTH; + std::unique_ptr<uint8_t[]> plaintext(new uint8_t[len]); + +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + // Fuzzer-mode tickets are unencrypted. + OPENSSL_memcpy(plaintext.get(), ciphertext, len); +#else + static const uint8_t kZeros[16] = {0}; + const uint8_t *iv = session->tlsext_tick + 16; + bssl::ScopedEVP_CIPHER_CTX ctx; + int len1, len2; + if (!EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cbc(), nullptr, kZeros, iv) || + !EVP_DecryptUpdate(ctx.get(), plaintext.get(), &len1, ciphertext, len) || + !EVP_DecryptFinal_ex(ctx.get(), plaintext.get() + len1, &len2)) { + return false; + } + + len = static_cast<size_t>(len1 + len2); +#endif + + bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLS_method())); + if (!ssl_ctx) { + return false; + } + bssl::UniquePtr<SSL_SESSION> server_session( + SSL_SESSION_from_bytes(plaintext.get(), len, ssl_ctx.get())); + if (!server_session) { + return false; + } + + *out = server_session->time; + return true; +} + +static bool TestSessionTimeout(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + if (!cert || !key) { + return false; + } + + for (bool server_test : std::vector<bool>{false, true}) { + static const time_t kStartTime = 1000; + g_current_time.tv_sec = kStartTime; + + // We are willing to use a longer lifetime for TLS 1.3 sessions as + // resumptions still perform ECDHE. + const time_t timeout = version == TLS1_3_VERSION + ? SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT + : SSL_DEFAULT_SESSION_TIMEOUT; + + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method)); + if (!server_ctx || !client_ctx || + !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) || + !SSL_CTX_set_min_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_min_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) { + return false; + } + + SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH); + SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH); + + // Both client and server must enforce session timeouts. We configure the + // other side with a frozen clock so it never expires tickets. + if (server_test) { + SSL_CTX_set_current_time_cb(client_ctx.get(), FrozenTimeCallback); + SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback); + } else { + SSL_CTX_set_current_time_cb(client_ctx.get(), CurrentTimeCallback); + SSL_CTX_set_current_time_cb(server_ctx.get(), FrozenTimeCallback); + } + + // Configure a ticket callback which renews tickets. + SSL_CTX_set_tlsext_ticket_key_cb(server_ctx.get(), RenewTicketCallback); + + bssl::UniquePtr<SSL_SESSION> session = + CreateClientSession(client_ctx.get(), server_ctx.get()); + if (!session) { + fprintf(stderr, "Error getting session.\n"); + return false; + } + + // Advance the clock just behind the timeout. + g_current_time.tv_sec += timeout - 1; + + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(), + true /* expect session reused */)) { + fprintf(stderr, "Error resuming session.\n"); + return false; + } + + // Advance the clock one more second. + g_current_time.tv_sec++; + + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(), + false /* expect session not reused */)) { + fprintf(stderr, "Error resuming session.\n"); + return false; + } + + // Rewind the clock to before the session was minted. + g_current_time.tv_sec = kStartTime - 1; + + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(), + false /* expect session not reused */)) { + fprintf(stderr, "Error resuming session.\n"); + return false; + } + + // SSL 3.0 cannot renew sessions. + if (version == SSL3_VERSION) { + continue; + } + + // Renew the session 10 seconds before expiration. + time_t new_start_time = kStartTime + timeout - 10; + g_current_time.tv_sec = new_start_time; + bssl::UniquePtr<SSL_SESSION> new_session = + ExpectSessionRenewed(client_ctx.get(), server_ctx.get(), session.get()); + if (!new_session) { + fprintf(stderr, "Error renewing session.\n"); + return false; + } + + // This new session is not the same object as before. + if (session.get() == new_session.get()) { + fprintf(stderr, "New and old sessions alias.\n"); + return false; + } + + // Check the sessions have timestamps measured from issuance. + long session_time = 0; + if (server_test) { + if (!GetServerTicketTime(&session_time, new_session.get())) { + fprintf(stderr, "Failed to decode session ticket.\n"); + return false; + } + } else { + session_time = new_session->time; + } + + if (session_time != g_current_time.tv_sec) { + fprintf(stderr, "New session is not measured from issuance.\n"); + return false; + } + + if (version == TLS1_3_VERSION) { + // Renewal incorporates fresh key material in TLS 1.3, so we extend the + // lifetime TLS 1.3. + g_current_time.tv_sec = new_start_time + timeout - 1; + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), + new_session.get(), + true /* expect session reused */)) { + fprintf(stderr, "Error resuming renewed session.\n"); + return false; + } + + // The new session expires after the new timeout. + g_current_time.tv_sec = new_start_time + timeout + 1; + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), + new_session.get(), + false /* expect session ot reused */)) { + fprintf(stderr, "Renewed session's lifetime is too long.\n"); + return false; + } + + // Renew the session until it begins just past the auth timeout. + time_t auth_end_time = kStartTime + SSL_DEFAULT_SESSION_AUTH_TIMEOUT; + while (new_start_time < auth_end_time - 1000) { + // Get as close as possible to target start time. + new_start_time = + std::min(auth_end_time - 1000, new_start_time + timeout - 1); + g_current_time.tv_sec = new_start_time; + new_session = ExpectSessionRenewed(client_ctx.get(), server_ctx.get(), + new_session.get()); + if (!new_session) { + fprintf(stderr, "Error renewing session.\n"); + return false; + } + } + + // Now the session's lifetime is bound by the auth timeout. + g_current_time.tv_sec = auth_end_time - 1; + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), + new_session.get(), + true /* expect session reused */)) { + fprintf(stderr, "Error resuming renewed session.\n"); + return false; + } + + g_current_time.tv_sec = auth_end_time + 1; + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), + new_session.get(), + false /* expect session ot reused */)) { + fprintf(stderr, "Renewed session's lifetime is too long.\n"); + return false; + } + } else { + // The new session is usable just before the old expiration. + g_current_time.tv_sec = kStartTime + timeout - 1; + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), + new_session.get(), + true /* expect session reused */)) { + fprintf(stderr, "Error resuming renewed session.\n"); + return false; + } + + // Renewal does not extend the lifetime, so it is not usable beyond the + // old expiration. + g_current_time.tv_sec = kStartTime + timeout + 1; + if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), + new_session.get(), + false /* expect session not reused */)) { + fprintf(stderr, "Renewed session's lifetime is too long.\n"); + return false; + } + } + } + + return true; +} + +static int SwitchContext(SSL *ssl, int *out_alert, void *arg) { + SSL_CTX *ctx = reinterpret_cast<SSL_CTX*>(arg); + SSL_set_SSL_CTX(ssl, ctx); + return SSL_TLSEXT_ERR_OK; +} + +static bool TestSNICallback(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + // SSL 3.0 lacks extensions. + if (version == SSL3_VERSION) { + return true; + } + + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + bssl::UniquePtr<X509> cert2 = GetECDSATestCertificate(); + bssl::UniquePtr<EVP_PKEY> key2 = GetECDSATestKey(); + if (!cert || !key || !cert2 || !key2) { + return false; + } + + // Test that switching the |SSL_CTX| at the SNI callback behaves correctly. + static const uint16_t kECDSAWithSHA256 = SSL_SIGN_ECDSA_SECP256R1_SHA256; + + static const uint8_t kSCTList[] = {0, 6, 0, 4, 5, 6, 7, 8}; + static const uint8_t kOCSPResponse[] = {1, 2, 3, 4}; + + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL_CTX> server_ctx2(SSL_CTX_new(method)); + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method)); + if (!server_ctx || !server_ctx2 || !client_ctx || + !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) || + !SSL_CTX_use_certificate(server_ctx2.get(), cert2.get()) || + !SSL_CTX_use_PrivateKey(server_ctx2.get(), key2.get()) || + !SSL_CTX_set_signed_cert_timestamp_list(server_ctx2.get(), kSCTList, + sizeof(kSCTList)) || + !SSL_CTX_set_ocsp_response(server_ctx2.get(), kOCSPResponse, + sizeof(kOCSPResponse)) || + // Historically signing preferences would be lost in some cases with the + // SNI callback, which triggers the TLS 1.2 SHA-1 default. To ensure + // this doesn't happen when |version| is TLS 1.2, configure the private + // key to only sign SHA-256. + !SSL_CTX_set_signing_algorithm_prefs(server_ctx2.get(), &kECDSAWithSHA256, + 1) || + !SSL_CTX_set_min_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_min_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_min_proto_version(server_ctx2.get(), version) || + !SSL_CTX_set_max_proto_version(server_ctx2.get(), version)) { + return false; + } + + SSL_CTX_set_tlsext_servername_callback(server_ctx.get(), SwitchContext); + SSL_CTX_set_tlsext_servername_arg(server_ctx.get(), server_ctx2.get()); + + SSL_CTX_enable_signed_cert_timestamps(client_ctx.get()); + SSL_CTX_enable_ocsp_stapling(client_ctx.get()); + + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get(), nullptr)) { + fprintf(stderr, "Handshake failed.\n"); + return false; + } + + // The client should have received |cert2|. + bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(client.get())); + if (!peer || X509_cmp(peer.get(), cert2.get()) != 0) { + fprintf(stderr, "Incorrect certificate received.\n"); + return false; + } + + // The client should have received |server_ctx2|'s SCT list. + const uint8_t *data; + size_t len; + SSL_get0_signed_cert_timestamp_list(client.get(), &data, &len); + if (Bytes(kSCTList) != Bytes(data, len)) { + fprintf(stderr, "Incorrect SCT list received.\n"); + return false; + } + + // The client should have received |server_ctx2|'s OCSP response. + SSL_get0_ocsp_response(client.get(), &data, &len); + if (Bytes(kOCSPResponse) != Bytes(data, len)) { + fprintf(stderr, "Incorrect OCSP response received.\n"); + return false; + } + + return true; +} + +// Test that the early callback can swap the maximum version. +TEST(SSLTest, EarlyCallbackVersionSwitch) { + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method())); + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(cert); + ASSERT_TRUE(key); + ASSERT_TRUE(server_ctx); + ASSERT_TRUE(client_ctx); + ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get())); + ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())); + ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION)); + ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION)); + + SSL_CTX_set_select_certificate_cb( + server_ctx.get(), [](const SSL_CLIENT_HELLO *client_hello) -> int { + if (!SSL_set_max_proto_version(client_hello->ssl, TLS1_2_VERSION)) { + return -1; + } + + return 1; + }); + + bssl::UniquePtr<SSL> client, server; + ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get(), nullptr)); + EXPECT_EQ(TLS1_2_VERSION, SSL_version(client.get())); +} + +TEST(SSLTest, SetVersion) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + + // Set valid TLS versions. + EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_VERSION)); + EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_1_VERSION)); + EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_VERSION)); + EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_1_VERSION)); + + // Invalid TLS versions are rejected. + EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_VERSION)); + EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0x0200)); + EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0x1234)); + EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_VERSION)); + EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0x0200)); + EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0x1234)); + + // Zero is the default version. + EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), 0)); + EXPECT_EQ(TLS1_2_VERSION, ctx->max_version); + EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), 0)); + EXPECT_EQ(SSL3_VERSION, ctx->min_version); + + ctx.reset(SSL_CTX_new(DTLS_method())); + ASSERT_TRUE(ctx); + + EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_VERSION)); + EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), DTLS1_2_VERSION)); + EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_VERSION)); + EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), DTLS1_2_VERSION)); + + EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_VERSION)); + EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0xfefe /* DTLS 1.1 */)); + EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0xfffe /* DTLS 0.1 */)); + EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), 0x1234)); + EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_VERSION)); + EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0xfefe /* DTLS 1.1 */)); + EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0xfffe /* DTLS 0.1 */)); + EXPECT_FALSE(SSL_CTX_set_min_proto_version(ctx.get(), 0x1234)); + + EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), 0)); + EXPECT_EQ(TLS1_2_VERSION, ctx->max_version); + EXPECT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), 0)); + EXPECT_EQ(TLS1_1_VERSION, ctx->min_version); +} + +static const char *GetVersionName(uint16_t version) { + switch (version) { + case SSL3_VERSION: + return "SSLv3"; + case TLS1_VERSION: + return "TLSv1"; + case TLS1_1_VERSION: + return "TLSv1.1"; + case TLS1_2_VERSION: + return "TLSv1.2"; + case TLS1_3_VERSION: + return "TLSv1.3"; + case DTLS1_VERSION: + return "DTLSv1"; + case DTLS1_2_VERSION: + return "DTLSv1.2"; + default: + return "???"; + } +} + +static bool TestVersion(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + if (!cert || !key) { + return false; + } + + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL> client, server; + if (!server_ctx || !client_ctx || + !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) || + !SSL_CTX_set_min_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_min_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(server_ctx.get(), version) || + !ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get(), nullptr /* no session */)) { + fprintf(stderr, "Failed to connect.\n"); + return false; + } + + if (SSL_version(client.get()) != version || + SSL_version(server.get()) != version) { + fprintf(stderr, "Version mismatch. Got %04x and %04x, wanted %04x.\n", + SSL_version(client.get()), SSL_version(server.get()), version); + return false; + } + + // Test the version name is reported as expected. + const char *version_name = GetVersionName(version); + if (strcmp(version_name, SSL_get_version(client.get())) != 0 || + strcmp(version_name, SSL_get_version(server.get())) != 0) { + fprintf(stderr, "Version name mismatch. Got '%s' and '%s', wanted '%s'.\n", + SSL_get_version(client.get()), SSL_get_version(server.get()), + version_name); + return false; + } + + // Test SSL_SESSION reports the same name. + const char *client_name = + SSL_SESSION_get_version(SSL_get_session(client.get())); + const char *server_name = + SSL_SESSION_get_version(SSL_get_session(server.get())); + if (strcmp(version_name, client_name) != 0 || + strcmp(version_name, server_name) != 0) { + fprintf(stderr, + "Session version name mismatch. Got '%s' and '%s', wanted '%s'.\n", + client_name, server_name, version_name); + return false; + } + + return true; +} + +// Tests that that |SSL_get_pending_cipher| is available during the ALPN +// selection callback. +static bool TestALPNCipherAvailable(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + // SSL 3.0 lacks extensions. + if (version == SSL3_VERSION) { + return true; + } + + static const uint8_t kALPNProtos[] = {0x03, 'f', 'o', 'o'}; + + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + if (!cert || !key) { + return false; + } + + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method)); + if (!ctx || !SSL_CTX_use_certificate(ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) || + !SSL_CTX_set_min_proto_version(ctx.get(), version) || + !SSL_CTX_set_max_proto_version(ctx.get(), version) || + SSL_CTX_set_alpn_protos(ctx.get(), kALPNProtos, sizeof(kALPNProtos)) != + 0) { + return false; + } + + // The ALPN callback does not fail the handshake on error, so have the + // callback write a boolean. + std::pair<uint16_t, bool> callback_state(version, false); + SSL_CTX_set_alpn_select_cb( + ctx.get(), + [](SSL *ssl, const uint8_t **out, uint8_t *out_len, const uint8_t *in, + unsigned in_len, void *arg) -> int { + auto state = reinterpret_cast<std::pair<uint16_t, bool> *>(arg); + if (SSL_get_pending_cipher(ssl) != nullptr && + SSL_version(ssl) == state->first) { + state->second = true; + } + return SSL_TLSEXT_ERR_NOACK; + }, + &callback_state); + + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(), + nullptr /* no session */)) { + return false; + } + + if (!callback_state.second) { + fprintf(stderr, "The pending cipher was not known in the ALPN callback.\n"); + return false; + } + + return true; +} + +static bool TestSSLClearSessionResumption(bool is_dtls, + const SSL_METHOD *method, + uint16_t version) { + // Skip this for TLS 1.3. TLS 1.3's ticket mechanism is incompatible with this + // API pattern. + if (version == TLS1_3_VERSION) { + return true; + } + + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method)); + if (!cert || !key || !server_ctx || !client_ctx || + !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) || + !SSL_CTX_set_min_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(client_ctx.get(), version) || + !SSL_CTX_set_min_proto_version(server_ctx.get(), version) || + !SSL_CTX_set_max_proto_version(server_ctx.get(), version)) { + return false; + } + + // Connect a client and a server. + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get(), nullptr /* no session */)) { + return false; + } + + if (SSL_session_reused(client.get()) || + SSL_session_reused(server.get())) { + fprintf(stderr, "Session unexpectedly reused.\n"); + return false; + } + + // Reset everything. + if (!SSL_clear(client.get()) || + !SSL_clear(server.get())) { + fprintf(stderr, "SSL_clear failed.\n"); + return false; + } + + // Attempt to connect a second time. + if (!CompleteHandshakes(client.get(), server.get())) { + fprintf(stderr, "Could not reuse SSL objects.\n"); + return false; + } + + // |SSL_clear| should implicitly offer the previous session to the server. + if (!SSL_session_reused(client.get()) || + !SSL_session_reused(server.get())) { + fprintf(stderr, "Session was not reused in second try.\n"); + return false; + } + + return true; +} + +static bool ChainsEqual(STACK_OF(X509) *chain, + const std::vector<X509 *> &expected) { + if (sk_X509_num(chain) != expected.size()) { + return false; + } + + for (size_t i = 0; i < expected.size(); i++) { + if (X509_cmp(sk_X509_value(chain, i), expected[i]) != 0) { + return false; + } + } + + return true; +} + +static bool TestAutoChain(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + bssl::UniquePtr<X509> cert = GetChainTestCertificate(); + bssl::UniquePtr<X509> intermediate = GetChainTestIntermediate(); + bssl::UniquePtr<EVP_PKEY> key = GetChainTestKey(); + if (!cert || !intermediate || !key) { + return false; + } + + // Configure both client and server to accept any certificate. Add + // |intermediate| to the cert store. + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method)); + if (!ctx || + !SSL_CTX_use_certificate(ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) || + !SSL_CTX_set_min_proto_version(ctx.get(), version) || + !SSL_CTX_set_max_proto_version(ctx.get(), version) || + !X509_STORE_add_cert(SSL_CTX_get_cert_store(ctx.get()), + intermediate.get())) { + return false; + } + SSL_CTX_set_verify( + ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); + SSL_CTX_set_cert_verify_callback(ctx.get(), VerifySucceed, NULL); + + // By default, the client and server should each only send the leaf. + bssl::UniquePtr<SSL> client, server; + if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(), + nullptr /* no session */)) { + return false; + } + + if (!ChainsEqual(SSL_get_peer_full_cert_chain(client.get()), {cert.get()})) { + fprintf(stderr, "Client-received chain did not match.\n"); + return false; + } + + if (!ChainsEqual(SSL_get_peer_full_cert_chain(server.get()), {cert.get()})) { + fprintf(stderr, "Server-received chain did not match.\n"); + return false; + } + + // If auto-chaining is enabled, then the intermediate is sent. + SSL_CTX_clear_mode(ctx.get(), SSL_MODE_NO_AUTO_CHAIN); + if (!ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(), + nullptr /* no session */)) { + return false; + } + + if (!ChainsEqual(SSL_get_peer_full_cert_chain(client.get()), + {cert.get(), intermediate.get()})) { + fprintf(stderr, "Client-received chain did not match (auto-chaining).\n"); + return false; + } + + if (!ChainsEqual(SSL_get_peer_full_cert_chain(server.get()), + {cert.get(), intermediate.get()})) { + fprintf(stderr, "Server-received chain did not match (auto-chaining).\n"); + return false; + } + + // Auto-chaining does not override explicitly-configured intermediates. + if (!SSL_CTX_add1_chain_cert(ctx.get(), cert.get()) || + !ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(), + nullptr /* no session */)) { + return false; + } + + if (!ChainsEqual(SSL_get_peer_full_cert_chain(client.get()), + {cert.get(), cert.get()})) { + fprintf(stderr, + "Client-received chain did not match (auto-chaining, explicit " + "intermediate).\n"); + return false; + } + + if (!ChainsEqual(SSL_get_peer_full_cert_chain(server.get()), + {cert.get(), cert.get()})) { + fprintf(stderr, + "Server-received chain did not match (auto-chaining, explicit " + "intermediate).\n"); + return false; + } + + return true; +} + +static bool ExpectBadWriteRetry() { + int err = ERR_get_error(); + if (ERR_GET_LIB(err) != ERR_LIB_SSL || + ERR_GET_REASON(err) != SSL_R_BAD_WRITE_RETRY) { + char buf[ERR_ERROR_STRING_BUF_LEN]; + ERR_error_string_n(err, buf, sizeof(buf)); + fprintf(stderr, "Wanted SSL_R_BAD_WRITE_RETRY, got: %s.\n", buf); + return false; + } + + if (ERR_peek_error() != 0) { + fprintf(stderr, "Unexpected error following SSL_R_BAD_WRITE_RETRY.\n"); + return false; + } + + return true; +} + +static bool TestSSLWriteRetry(bool is_dtls, const SSL_METHOD *method, + uint16_t version) { + if (is_dtls) { + return true; + } + + for (bool enable_partial_write : std::vector<bool>{false, true}) { + // Connect a client and server. + bssl::UniquePtr<X509> cert = GetTestCertificate(); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(method)); + bssl::UniquePtr<SSL> client, server; + if (!cert || !key || !ctx || + !SSL_CTX_use_certificate(ctx.get(), cert.get()) || + !SSL_CTX_use_PrivateKey(ctx.get(), key.get()) || + !SSL_CTX_set_min_proto_version(ctx.get(), version) || + !SSL_CTX_set_max_proto_version(ctx.get(), version) || + !ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(), + nullptr /* no session */)) { + return false; + } + + if (enable_partial_write) { + SSL_set_mode(client.get(), SSL_MODE_ENABLE_PARTIAL_WRITE); + } + + // Write without reading until the buffer is full and we have an unfinished + // write. Keep a count so we may reread it again later. "hello!" will be + // written in two chunks, "hello" and "!". + char data[] = "hello!"; + static const int kChunkLen = 5; // The length of "hello". + unsigned count = 0; + for (;;) { + int ret = SSL_write(client.get(), data, kChunkLen); + if (ret <= 0) { + int err = SSL_get_error(client.get(), ret); + if (SSL_get_error(client.get(), ret) == SSL_ERROR_WANT_WRITE) { + break; + } + fprintf(stderr, "SSL_write failed in unexpected way: %d\n", err); + return false; + } + + if (ret != 5) { + fprintf(stderr, "SSL_write wrote %d bytes, expected 5.\n", ret); + return false; + } + + count++; + } + + // Retrying with the same parameters is legal. + if (SSL_get_error(client.get(), SSL_write(client.get(), data, kChunkLen)) != + SSL_ERROR_WANT_WRITE) { + fprintf(stderr, "SSL_write retry unexpectedly failed.\n"); + return false; + } + + // Retrying with the same buffer but shorter length is not legal. + if (SSL_get_error(client.get(), + SSL_write(client.get(), data, kChunkLen - 1)) != + SSL_ERROR_SSL || + !ExpectBadWriteRetry()) { + fprintf(stderr, "SSL_write retry did not fail as expected.\n"); + return false; + } + + // Retrying with a different buffer pointer is not legal. + char data2[] = "hello"; + if (SSL_get_error(client.get(), SSL_write(client.get(), data2, + kChunkLen)) != SSL_ERROR_SSL || + !ExpectBadWriteRetry()) { + fprintf(stderr, "SSL_write retry did not fail as expected.\n"); + return false; + } + + // With |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER|, the buffer may move. + SSL_set_mode(client.get(), SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + if (SSL_get_error(client.get(), + SSL_write(client.get(), data2, kChunkLen)) != + SSL_ERROR_WANT_WRITE) { + fprintf(stderr, "SSL_write retry unexpectedly failed.\n"); + return false; + } + + // |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER| does not disable length checks. + if (SSL_get_error(client.get(), + SSL_write(client.get(), data2, kChunkLen - 1)) != + SSL_ERROR_SSL || + !ExpectBadWriteRetry()) { + fprintf(stderr, "SSL_write retry did not fail as expected.\n"); + return false; + } + + // Retrying with a larger buffer is legal. + if (SSL_get_error(client.get(), + SSL_write(client.get(), data, kChunkLen + 1)) != + SSL_ERROR_WANT_WRITE) { + fprintf(stderr, "SSL_write retry unexpectedly failed.\n"); + return false; + } + + // Drain the buffer. + char buf[20]; + for (unsigned i = 0; i < count; i++) { + if (SSL_read(server.get(), buf, sizeof(buf)) != kChunkLen || + OPENSSL_memcmp(buf, "hello", kChunkLen) != 0) { + fprintf(stderr, "Failed to read initial records.\n"); + return false; + } + } + + // Now that there is space, a retry with a larger buffer should flush the + // pending record, skip over that many bytes of input (on assumption they + // are the same), and write the remainder. If SSL_MODE_ENABLE_PARTIAL_WRITE + // is set, this will complete in two steps. + char data3[] = "_____!"; + if (enable_partial_write) { + if (SSL_write(client.get(), data3, kChunkLen + 1) != kChunkLen || + SSL_write(client.get(), data3 + kChunkLen, 1) != 1) { + fprintf(stderr, "SSL_write retry failed.\n"); + return false; + } + } else if (SSL_write(client.get(), data3, kChunkLen + 1) != kChunkLen + 1) { + fprintf(stderr, "SSL_write retry failed.\n"); + return false; + } + + // Check the last write was correct. The data will be spread over two + // records, so SSL_read returns twice. + if (SSL_read(server.get(), buf, sizeof(buf)) != kChunkLen || + OPENSSL_memcmp(buf, "hello", kChunkLen) != 0 || + SSL_read(server.get(), buf, sizeof(buf)) != 1 || + buf[0] != '!') { + fprintf(stderr, "Failed to read write retry.\n"); + return false; + } + } + + return true; +} + +static bool ForEachVersion(bool (*test_func)(bool is_dtls, + const SSL_METHOD *method, + uint16_t version)) { + static uint16_t kTLSVersions[] = { + SSL3_VERSION, + TLS1_VERSION, + TLS1_1_VERSION, + TLS1_2_VERSION, +// TLS 1.3 requires RSA-PSS, which is disabled for Android system builds. +#if !defined(BORINGSSL_ANDROID_SYSTEM) + TLS1_3_VERSION, +#endif + }; + + static uint16_t kDTLSVersions[] = { + DTLS1_VERSION, DTLS1_2_VERSION, + }; + + for (uint16_t version : kTLSVersions) { + if (!test_func(false, TLS_method(), version)) { + fprintf(stderr, "Test failed at TLS version %04x.\n", version); + return false; + } + } + + for (uint16_t version : kDTLSVersions) { + if (!test_func(true, DTLS_method(), version)) { + fprintf(stderr, "Test failed at DTLS version %04x.\n", version); + return false; + } + } + + return true; +} + +TEST(SSLTest, AddChainCertHack) { + // Ensure that we don't accidently break the hack that we have in place to + // keep curl and serf happy when they use an |X509| even after transfering + // ownership. + + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + X509 *cert = GetTestCertificate().release(); + ASSERT_TRUE(cert); + SSL_CTX_add0_chain_cert(ctx.get(), cert); + + // This should not trigger a use-after-free. + X509_cmp(cert, cert); +} + +TEST(SSLTest, GetCertificate) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + bssl::UniquePtr<X509> cert = GetTestCertificate(); + ASSERT_TRUE(cert); + ASSERT_TRUE(SSL_CTX_use_certificate(ctx.get(), cert.get())); + bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get())); + ASSERT_TRUE(ssl); + + X509 *cert2 = SSL_CTX_get0_certificate(ctx.get()); + ASSERT_TRUE(cert2); + X509 *cert3 = SSL_get_certificate(ssl.get()); + ASSERT_TRUE(cert3); + + // The old and new certificates must be identical. + EXPECT_EQ(0, X509_cmp(cert.get(), cert2)); + EXPECT_EQ(0, X509_cmp(cert.get(), cert3)); + + uint8_t *der = nullptr; + long der_len = i2d_X509(cert.get(), &der); + ASSERT_LT(0, der_len); + bssl::UniquePtr<uint8_t> free_der(der); + + uint8_t *der2 = nullptr; + long der2_len = i2d_X509(cert2, &der2); + ASSERT_LT(0, der2_len); + bssl::UniquePtr<uint8_t> free_der2(der2); + + uint8_t *der3 = nullptr; + long der3_len = i2d_X509(cert3, &der3); + ASSERT_LT(0, der3_len); + bssl::UniquePtr<uint8_t> free_der3(der3); + + // They must also encode identically. + EXPECT_EQ(Bytes(der, der_len), Bytes(der2, der2_len)); + EXPECT_EQ(Bytes(der, der_len), Bytes(der3, der3_len)); +} + +TEST(SSLTest, SetChainAndKeyMismatch) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_with_buffers_method())); + ASSERT_TRUE(ctx); + + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + ASSERT_TRUE(key); + bssl::UniquePtr<CRYPTO_BUFFER> leaf = GetChainTestCertificateBuffer(); + ASSERT_TRUE(leaf); + std::vector<CRYPTO_BUFFER*> chain = { + leaf.get(), + }; + + // Should fail because |GetTestKey| doesn't match the chain-test certificate. + ASSERT_FALSE(SSL_CTX_set_chain_and_key(ctx.get(), &chain[0], chain.size(), + key.get(), nullptr)); + ERR_clear_error(); +} + +TEST(SSLTest, SetChainAndKey) { + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_with_buffers_method())); + ASSERT_TRUE(client_ctx); + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_with_buffers_method())); + ASSERT_TRUE(server_ctx); + + bssl::UniquePtr<EVP_PKEY> key = GetChainTestKey(); + ASSERT_TRUE(key); + bssl::UniquePtr<CRYPTO_BUFFER> leaf = GetChainTestCertificateBuffer(); + ASSERT_TRUE(leaf); + bssl::UniquePtr<CRYPTO_BUFFER> intermediate = + GetChainTestIntermediateBuffer(); + ASSERT_TRUE(intermediate); + std::vector<CRYPTO_BUFFER*> chain = { + leaf.get(), intermediate.get(), + }; + ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), &chain[0], + chain.size(), key.get(), nullptr)); + + SSL_CTX_i_promise_to_verify_certs_after_the_handshake(client_ctx.get()); + + bssl::UniquePtr<SSL> client, server; + ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get(), + nullptr /* no session */)); +} + +// Configuring the empty cipher list, though an error, should still modify the +// configuration. +TEST(SSLTest, EmptyCipherList) { + bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(ctx); + + // Initially, the cipher list is not empty. + EXPECT_NE(0u, sk_SSL_CIPHER_num(SSL_CTX_get_ciphers(ctx.get()))); + + // Configuring the empty cipher list fails. + EXPECT_FALSE(SSL_CTX_set_cipher_list(ctx.get(), "")); + ERR_clear_error(); + + // But the cipher list is still updated to empty. + EXPECT_EQ(0u, sk_SSL_CIPHER_num(SSL_CTX_get_ciphers(ctx.get()))); +} + +// ssl_test_ticket_aead_failure_mode enumerates the possible ways in which the +// test |SSL_TICKET_AEAD_METHOD| can fail. +enum ssl_test_ticket_aead_failure_mode { + ssl_test_ticket_aead_ok = 0, + ssl_test_ticket_aead_seal_fail, + ssl_test_ticket_aead_open_soft_fail, + ssl_test_ticket_aead_open_hard_fail, +}; + +struct ssl_test_ticket_aead_state { + unsigned retry_count; + ssl_test_ticket_aead_failure_mode failure_mode; +}; + +static int ssl_test_ticket_aead_ex_index_dup(CRYPTO_EX_DATA *to, + const CRYPTO_EX_DATA *from, + void **from_d, int index, + long argl, void *argp) { + abort(); +} + +static void ssl_test_ticket_aead_ex_index_free(void *parent, void *ptr, + CRYPTO_EX_DATA *ad, int index, + long argl, void *argp) { + auto state = reinterpret_cast<ssl_test_ticket_aead_state*>(ptr); + if (state == nullptr) { + return; + } + + OPENSSL_free(state); +} + +static CRYPTO_once_t g_ssl_test_ticket_aead_ex_index_once = CRYPTO_ONCE_INIT; +static int g_ssl_test_ticket_aead_ex_index; + +static int ssl_test_ticket_aead_get_ex_index() { + CRYPTO_once(&g_ssl_test_ticket_aead_ex_index_once, [] { + g_ssl_test_ticket_aead_ex_index = SSL_get_ex_new_index( + 0, nullptr, nullptr, ssl_test_ticket_aead_ex_index_dup, + ssl_test_ticket_aead_ex_index_free); + }); + return g_ssl_test_ticket_aead_ex_index; +} + +static size_t ssl_test_ticket_aead_max_overhead(SSL *ssl) { + return 1; +} + +static int ssl_test_ticket_aead_seal(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out_len, const uint8_t *in, + size_t in_len) { + auto state = reinterpret_cast<ssl_test_ticket_aead_state *>( + SSL_get_ex_data(ssl, ssl_test_ticket_aead_get_ex_index())); + + if (state->failure_mode == ssl_test_ticket_aead_seal_fail || + max_out_len < in_len + 1) { + return 0; + } + + OPENSSL_memmove(out, in, in_len); + out[in_len] = 0xff; + *out_len = in_len + 1; + + return 1; +} + +static ssl_ticket_aead_result_t ssl_test_ticket_aead_open( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len, + const uint8_t *in, size_t in_len) { + auto state = reinterpret_cast<ssl_test_ticket_aead_state *>( + SSL_get_ex_data(ssl, ssl_test_ticket_aead_get_ex_index())); + + if (state->retry_count > 0) { + state->retry_count--; + return ssl_ticket_aead_retry; + } + + switch (state->failure_mode) { + case ssl_test_ticket_aead_ok: + break; + case ssl_test_ticket_aead_seal_fail: + // If |seal| failed then there shouldn't be any ticket to try and + // decrypt. + abort(); + break; + case ssl_test_ticket_aead_open_soft_fail: + return ssl_ticket_aead_ignore_ticket; + case ssl_test_ticket_aead_open_hard_fail: + return ssl_ticket_aead_error; + } + + if (in_len == 0 || in[in_len - 1] != 0xff) { + return ssl_ticket_aead_ignore_ticket; + } + + if (max_out_len < in_len - 1) { + return ssl_ticket_aead_error; + } + + OPENSSL_memmove(out, in, in_len - 1); + *out_len = in_len - 1; + return ssl_ticket_aead_success; +} + +static const SSL_TICKET_AEAD_METHOD kSSLTestTicketMethod = { + ssl_test_ticket_aead_max_overhead, + ssl_test_ticket_aead_seal, + ssl_test_ticket_aead_open, +}; + +static void ConnectClientAndServerWithTicketMethod( + bssl::UniquePtr<SSL> *out_client, bssl::UniquePtr<SSL> *out_server, + SSL_CTX *client_ctx, SSL_CTX *server_ctx, unsigned retry_count, + ssl_test_ticket_aead_failure_mode failure_mode, SSL_SESSION *session) { + bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx)); + ASSERT_TRUE(client); + ASSERT_TRUE(server); + SSL_set_connect_state(client.get()); + SSL_set_accept_state(server.get()); + + auto state = reinterpret_cast<ssl_test_ticket_aead_state *>( + OPENSSL_malloc(sizeof(ssl_test_ticket_aead_state))); + ASSERT_TRUE(state); + OPENSSL_memset(state, 0, sizeof(ssl_test_ticket_aead_state)); + state->retry_count = retry_count; + state->failure_mode = failure_mode; + + ASSERT_TRUE(SSL_set_ex_data(server.get(), ssl_test_ticket_aead_get_ex_index(), + state)); + + SSL_set_session(client.get(), session); + + BIO *bio1, *bio2; + ASSERT_TRUE(BIO_new_bio_pair(&bio1, 0, &bio2, 0)); + + // SSL_set_bio takes ownership. + SSL_set_bio(client.get(), bio1, bio1); + SSL_set_bio(server.get(), bio2, bio2); + + if (CompleteHandshakes(client.get(), server.get())) { + *out_client = std::move(client); + *out_server = std::move(server); + } else { + out_client->reset(); + out_server->reset(); + } +} + +class TicketAEADMethodTest + : public ::testing::TestWithParam<testing::tuple< + uint16_t, unsigned, ssl_test_ticket_aead_failure_mode>> {}; + +TEST_P(TicketAEADMethodTest, Resume) { + bssl::UniquePtr<X509> cert = GetTestCertificate(); + ASSERT_TRUE(cert); + bssl::UniquePtr<EVP_PKEY> key = GetTestKey(); + ASSERT_TRUE(key); + + bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(server_ctx); + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); + ASSERT_TRUE(client_ctx); + + const uint16_t version = testing::get<0>(GetParam()); + const unsigned retry_count = testing::get<1>(GetParam()); + const ssl_test_ticket_aead_failure_mode failure_mode = + testing::get<2>(GetParam()); + + ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get())); + ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get())); + ASSERT_TRUE(SSL_CTX_set_min_proto_version(client_ctx.get(), version)); + ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), version)); + ASSERT_TRUE(SSL_CTX_set_min_proto_version(server_ctx.get(), version)); + ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), version)); + + SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH); + SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH); + SSL_CTX_set_current_time_cb(client_ctx.get(), FrozenTimeCallback); + SSL_CTX_set_current_time_cb(server_ctx.get(), FrozenTimeCallback); + SSL_CTX_sess_set_new_cb(client_ctx.get(), SaveLastSession); + + SSL_CTX_set_ticket_aead_method(server_ctx.get(), &kSSLTestTicketMethod); + + bssl::UniquePtr<SSL> client, server; + ConnectClientAndServerWithTicketMethod(&client, &server, client_ctx.get(), + server_ctx.get(), retry_count, + failure_mode, nullptr); + switch (failure_mode) { + case ssl_test_ticket_aead_ok: + case ssl_test_ticket_aead_open_hard_fail: + case ssl_test_ticket_aead_open_soft_fail: + ASSERT_TRUE(client); + break; + case ssl_test_ticket_aead_seal_fail: + EXPECT_FALSE(client); + return; + } + EXPECT_FALSE(SSL_session_reused(client.get())); + EXPECT_FALSE(SSL_session_reused(server.get())); + + // Run the read loop to account for post-handshake tickets in TLS 1.3. + SSL_read(client.get(), nullptr, 0); + + bssl::UniquePtr<SSL_SESSION> session = std::move(g_last_session); + ConnectClientAndServerWithTicketMethod(&client, &server, client_ctx.get(), + server_ctx.get(), retry_count, + failure_mode, session.get()); + switch (failure_mode) { + case ssl_test_ticket_aead_ok: + ASSERT_TRUE(client); + EXPECT_TRUE(SSL_session_reused(client.get())); + EXPECT_TRUE(SSL_session_reused(server.get())); + break; + case ssl_test_ticket_aead_seal_fail: + abort(); + break; + case ssl_test_ticket_aead_open_hard_fail: + EXPECT_FALSE(client); + break; + case ssl_test_ticket_aead_open_soft_fail: + ASSERT_TRUE(client); + EXPECT_FALSE(SSL_session_reused(client.get())); + EXPECT_FALSE(SSL_session_reused(server.get())); + } +} + +INSTANTIATE_TEST_CASE_P( + TicketAEADMethodTests, TicketAEADMethodTest, + testing::Combine( + testing::Values(TLS1_2_VERSION, TLS1_3_VERSION), + testing::Values(0, 1, 2), + testing::Values(ssl_test_ticket_aead_ok, + ssl_test_ticket_aead_seal_fail, + ssl_test_ticket_aead_open_soft_fail, + ssl_test_ticket_aead_open_hard_fail))); + +// TODO(davidben): Convert this file to GTest properly. +TEST(SSLTest, AllTests) { + if (!TestCipherRules() || + !TestCurveRules() || + !TestSSL_SESSIONEncoding(kOpenSSLSession) || + !TestSSL_SESSIONEncoding(kCustomSession) || + !TestSSL_SESSIONEncoding(kBoringSSLSession) || + !TestBadSSL_SESSIONEncoding(kBadSessionExtraField) || + !TestBadSSL_SESSIONEncoding(kBadSessionVersion) || + !TestBadSSL_SESSIONEncoding(kBadSessionTrailingData) || + // TODO(svaldez): Update this when TLS 1.3 is enabled by default. + !TestDefaultVersion(SSL3_VERSION, TLS1_2_VERSION, &TLS_method) || + !TestDefaultVersion(SSL3_VERSION, SSL3_VERSION, &SSLv3_method) || + !TestDefaultVersion(TLS1_VERSION, TLS1_VERSION, &TLSv1_method) || + !TestDefaultVersion(TLS1_1_VERSION, TLS1_1_VERSION, &TLSv1_1_method) || + !TestDefaultVersion(TLS1_2_VERSION, TLS1_2_VERSION, &TLSv1_2_method) || + !TestDefaultVersion(TLS1_1_VERSION, TLS1_2_VERSION, &DTLS_method) || + !TestDefaultVersion(TLS1_1_VERSION, TLS1_1_VERSION, &DTLSv1_method) || + !TestDefaultVersion(TLS1_2_VERSION, TLS1_2_VERSION, &DTLSv1_2_method) || + !TestCipherGetRFCName() || + // Test the padding extension at TLS 1.2. + !TestPaddingExtension(TLS1_2_VERSION, TLS1_2_VERSION) || + // Test the padding extension at TLS 1.3 with a TLS 1.2 session, so there + // will be no PSK binder after the padding extension. + !TestPaddingExtension(TLS1_3_VERSION, TLS1_2_VERSION) || + // Test the padding extension at TLS 1.3 with a TLS 1.3 session, so there + // will be a PSK binder after the padding extension. + !TestPaddingExtension(TLS1_3_VERSION, TLS1_3_DRAFT_VERSION) || + !TestInternalSessionCache() || + !ForEachVersion(TestSequenceNumber) || + !ForEachVersion(TestOneSidedShutdown) || + !ForEachVersion(TestGetPeerCertificate) || + !ForEachVersion(TestRetainOnlySHA256OfCerts) || + !TestClientHello() || + !ForEachVersion(TestSessionIDContext) || + !ForEachVersion(TestSessionTimeout) || + !ForEachVersion(TestSNICallback) || + !ForEachVersion(TestVersion) || + !ForEachVersion(TestALPNCipherAvailable) || + !ForEachVersion(TestSSLClearSessionResumption) || + !ForEachVersion(TestAutoChain) || + !ForEachVersion(TestSSLWriteRetry)) { + ADD_FAILURE() << "Tests failed"; + } +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_transcript.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_transcript.c new file mode 100644 index 000000000..9cc37778c --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_transcript.c @@ -0,0 +1,405 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/digest.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/md5.h> +#include <openssl/nid.h> +#include <openssl/sha.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +int SSL_TRANSCRIPT_init(SSL_TRANSCRIPT *transcript) { + SSL_TRANSCRIPT_cleanup(transcript); + transcript->buffer = BUF_MEM_new(); + return transcript->buffer != NULL; +} + +/* init_digest_with_data calls |EVP_DigestInit_ex| on |ctx| with |md| and then + * writes the data in |buf| to it. */ +static int init_digest_with_data(EVP_MD_CTX *ctx, const EVP_MD *md, + const BUF_MEM *buf) { + if (!EVP_DigestInit_ex(ctx, md, NULL)) { + return 0; + } + EVP_DigestUpdate(ctx, buf->data, buf->length); + return 1; +} + +int SSL_TRANSCRIPT_init_hash(SSL_TRANSCRIPT *transcript, uint16_t version, + int algorithm_prf) { + const EVP_MD *md = ssl_get_handshake_digest(algorithm_prf, version); + + /* To support SSL 3.0's Finished and CertificateVerify constructions, + * EVP_md5_sha1() is split into MD5 and SHA-1 halves. When SSL 3.0 is removed, + * we can simplify this. */ + if (md == EVP_md5_sha1()) { + if (!init_digest_with_data(&transcript->md5, EVP_md5(), + transcript->buffer)) { + return 0; + } + md = EVP_sha1(); + } + + if (!init_digest_with_data(&transcript->hash, md, transcript->buffer)) { + return 0; + } + + return 1; +} + +void SSL_TRANSCRIPT_cleanup(SSL_TRANSCRIPT *transcript) { + SSL_TRANSCRIPT_free_buffer(transcript); + EVP_MD_CTX_cleanup(&transcript->hash); + EVP_MD_CTX_cleanup(&transcript->md5); +} + +void SSL_TRANSCRIPT_free_buffer(SSL_TRANSCRIPT *transcript) { + BUF_MEM_free(transcript->buffer); + transcript->buffer = NULL; +} + +size_t SSL_TRANSCRIPT_digest_len(const SSL_TRANSCRIPT *transcript) { + return EVP_MD_size(SSL_TRANSCRIPT_md(transcript)); +} + +const EVP_MD *SSL_TRANSCRIPT_md(const SSL_TRANSCRIPT *transcript) { + if (EVP_MD_CTX_md(&transcript->md5) != NULL) { + return EVP_md5_sha1(); + } + return EVP_MD_CTX_md(&transcript->hash); +} + +int SSL_TRANSCRIPT_update(SSL_TRANSCRIPT *transcript, const uint8_t *in, + size_t in_len) { + /* Depending on the state of the handshake, either the handshake buffer may be + * active, the rolling hash, or both. */ + if (transcript->buffer != NULL) { + size_t new_len = transcript->buffer->length + in_len; + if (new_len < in_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + if (!BUF_MEM_grow(transcript->buffer, new_len)) { + return 0; + } + OPENSSL_memcpy(transcript->buffer->data + new_len - in_len, in, in_len); + } + + if (EVP_MD_CTX_md(&transcript->hash) != NULL) { + EVP_DigestUpdate(&transcript->hash, in, in_len); + } + if (EVP_MD_CTX_md(&transcript->md5) != NULL) { + EVP_DigestUpdate(&transcript->md5, in, in_len); + } + + return 1; +} + +int SSL_TRANSCRIPT_get_hash(const SSL_TRANSCRIPT *transcript, uint8_t *out, + size_t *out_len) { + int ret = 0; + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); + unsigned md5_len = 0; + if (EVP_MD_CTX_md(&transcript->md5) != NULL) { + if (!EVP_MD_CTX_copy_ex(&ctx, &transcript->md5) || + !EVP_DigestFinal_ex(&ctx, out, &md5_len)) { + goto err; + } + } + + unsigned len; + if (!EVP_MD_CTX_copy_ex(&ctx, &transcript->hash) || + !EVP_DigestFinal_ex(&ctx, out + md5_len, &len)) { + goto err; + } + + *out_len = md5_len + len; + ret = 1; + +err: + EVP_MD_CTX_cleanup(&ctx); + return ret; +} + +static int ssl3_handshake_mac(SSL_TRANSCRIPT *transcript, + const SSL_SESSION *session, + const EVP_MD_CTX *ctx_template, + const char *sender, size_t sender_len, + uint8_t *p, size_t *out_len) { + unsigned int len; + size_t npad, n; + unsigned int i; + uint8_t md_buf[EVP_MAX_MD_SIZE]; + EVP_MD_CTX ctx; + + EVP_MD_CTX_init(&ctx); + if (!EVP_MD_CTX_copy_ex(&ctx, ctx_template)) { + EVP_MD_CTX_cleanup(&ctx); + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); + return 0; + } + + static const uint8_t kPad1[48] = { + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + }; + + static const uint8_t kPad2[48] = { + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, + }; + + n = EVP_MD_CTX_size(&ctx); + + npad = (48 / n) * n; + if (sender != NULL) { + EVP_DigestUpdate(&ctx, sender, sender_len); + } + EVP_DigestUpdate(&ctx, session->master_key, session->master_key_length); + EVP_DigestUpdate(&ctx, kPad1, npad); + EVP_DigestFinal_ex(&ctx, md_buf, &i); + + if (!EVP_DigestInit_ex(&ctx, EVP_MD_CTX_md(&ctx), NULL)) { + EVP_MD_CTX_cleanup(&ctx); + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); + return 0; + } + EVP_DigestUpdate(&ctx, session->master_key, session->master_key_length); + EVP_DigestUpdate(&ctx, kPad2, npad); + EVP_DigestUpdate(&ctx, md_buf, i); + EVP_DigestFinal_ex(&ctx, p, &len); + + EVP_MD_CTX_cleanup(&ctx); + + *out_len = len; + return 1; +} + +int SSL_TRANSCRIPT_ssl3_cert_verify_hash(SSL_TRANSCRIPT *transcript, + uint8_t *out, size_t *out_len, + const SSL_SESSION *session, + int signature_algorithm) { + if (SSL_TRANSCRIPT_md(transcript) != EVP_md5_sha1()) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) { + size_t md5_len, len; + if (!ssl3_handshake_mac(transcript, session, &transcript->md5, NULL, 0, out, + &md5_len) || + !ssl3_handshake_mac(transcript, session, &transcript->hash, NULL, 0, + out + md5_len, &len)) { + return 0; + } + *out_len = md5_len + len; + return 1; + } + + if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) { + return ssl3_handshake_mac(transcript, session, &transcript->hash, NULL, 0, + out, out_len); + } + + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; +} + +int SSL_TRANSCRIPT_finish_mac(SSL_TRANSCRIPT *transcript, uint8_t *out, + size_t *out_len, const SSL_SESSION *session, + int from_server, uint16_t version) { + if (version == SSL3_VERSION) { + if (SSL_TRANSCRIPT_md(transcript) != EVP_md5_sha1()) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST + : SSL3_MD_CLIENT_FINISHED_CONST; + const size_t sender_len = 4; + size_t md5_len, len; + if (!ssl3_handshake_mac(transcript, session, &transcript->md5, sender, + sender_len, out, &md5_len) || + !ssl3_handshake_mac(transcript, session, &transcript->hash, sender, + sender_len, out + md5_len, &len)) { + return 0; + } + + *out_len = md5_len + len; + return 1; + } + + /* At this point, the handshake should have released the handshake buffer on + * its own. */ + assert(transcript->buffer == NULL); + + const char *label = TLS_MD_CLIENT_FINISH_CONST; + size_t label_len = TLS_MD_SERVER_FINISH_CONST_SIZE; + if (from_server) { + label = TLS_MD_SERVER_FINISH_CONST; + label_len = TLS_MD_SERVER_FINISH_CONST_SIZE; + } + + uint8_t digests[EVP_MAX_MD_SIZE]; + size_t digests_len; + if (!SSL_TRANSCRIPT_get_hash(transcript, digests, &digests_len)) { + return 0; + } + + static const size_t kFinishedLen = 12; + if (!tls1_prf(SSL_TRANSCRIPT_md(transcript), out, kFinishedLen, + session->master_key, session->master_key_length, label, + label_len, digests, digests_len, NULL, 0)) { + return 0; + } + + *out_len = kFinishedLen; + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_x509.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_x509.c new file mode 100644 index 000000000..65405aafd --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/ssl_x509.c @@ -0,0 +1,1344 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. + * ECC cipher suite support in OpenSSL originally developed by + * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <assert.h> + +#include <openssl/asn1.h> +#include <openssl/bytestring.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/stack.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/x509_vfy.h> + +#include "internal.h" +#include "../crypto/internal.h" + + +/* check_ssl_x509_method asserts that |ssl| has the X509-based method + * installed. Calling an X509-based method on an |ssl| with a different method + * will likely misbehave and possibly crash or leak memory. */ +static void check_ssl_x509_method(const SSL *ssl) { + assert(ssl == NULL || ssl->ctx->x509_method == &ssl_crypto_x509_method); +} + +/* check_ssl_ctx_x509_method acts like |check_ssl_x509_method|, but for an + * |SSL_CTX|. */ +static void check_ssl_ctx_x509_method(const SSL_CTX *ctx) { + assert(ctx == NULL || ctx->x509_method == &ssl_crypto_x509_method); +} + +X509 *SSL_get_peer_certificate(const SSL *ssl) { + check_ssl_x509_method(ssl); + if (ssl == NULL) { + return NULL; + } + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL || session->x509_peer == NULL) { + return NULL; + } + X509_up_ref(session->x509_peer); + return session->x509_peer; +} + +STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *ssl) { + check_ssl_x509_method(ssl); + if (ssl == NULL) { + return NULL; + } + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL || + session->x509_chain == NULL) { + return NULL; + } + + if (!ssl->server) { + return session->x509_chain; + } + + /* OpenSSL historically didn't include the leaf certificate in the returned + * certificate chain, but only for servers. */ + if (session->x509_chain_without_leaf == NULL) { + session->x509_chain_without_leaf = sk_X509_new_null(); + if (session->x509_chain_without_leaf == NULL) { + return NULL; + } + + for (size_t i = 1; i < sk_X509_num(session->x509_chain); i++) { + X509 *cert = sk_X509_value(session->x509_chain, i); + if (!sk_X509_push(session->x509_chain_without_leaf, cert)) { + sk_X509_pop_free(session->x509_chain_without_leaf, X509_free); + session->x509_chain_without_leaf = NULL; + return NULL; + } + X509_up_ref(cert); + } + } + + return session->x509_chain_without_leaf; +} + +STACK_OF(X509) *SSL_get_peer_full_cert_chain(const SSL *ssl) { + check_ssl_x509_method(ssl); + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL) { + return NULL; + } + + return session->x509_chain; +} + +int SSL_CTX_set_purpose(SSL_CTX *ctx, int purpose) { + check_ssl_ctx_x509_method(ctx); + return X509_VERIFY_PARAM_set_purpose(ctx->param, purpose); +} + +int SSL_set_purpose(SSL *ssl, int purpose) { + check_ssl_x509_method(ssl); + return X509_VERIFY_PARAM_set_purpose(ssl->param, purpose); +} + +int SSL_CTX_set_trust(SSL_CTX *ctx, int trust) { + check_ssl_ctx_x509_method(ctx); + return X509_VERIFY_PARAM_set_trust(ctx->param, trust); +} + +int SSL_set_trust(SSL *ssl, int trust) { + check_ssl_x509_method(ssl); + return X509_VERIFY_PARAM_set_trust(ssl->param, trust); +} + +int SSL_CTX_set1_param(SSL_CTX *ctx, const X509_VERIFY_PARAM *param) { + check_ssl_ctx_x509_method(ctx); + return X509_VERIFY_PARAM_set1(ctx->param, param); +} + +int SSL_set1_param(SSL *ssl, const X509_VERIFY_PARAM *param) { + check_ssl_x509_method(ssl); + return X509_VERIFY_PARAM_set1(ssl->param, param); +} + +X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + return ctx->param; +} + +X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) { + check_ssl_x509_method(ssl); + return ssl->param; +} + +int SSL_get_verify_depth(const SSL *ssl) { + check_ssl_x509_method(ssl); + return X509_VERIFY_PARAM_get_depth(ssl->param); +} + +int (*SSL_get_verify_callback(const SSL *ssl))(int, X509_STORE_CTX *) { + check_ssl_x509_method(ssl); + return ssl->verify_callback; +} + +int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + return ctx->verify_mode; +} + +int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + return X509_VERIFY_PARAM_get_depth(ctx->param); +} + +int (*SSL_CTX_get_verify_callback(const SSL_CTX *ctx))( + int ok, X509_STORE_CTX *store_ctx) { + check_ssl_ctx_x509_method(ctx); + return ctx->default_verify_callback; +} + +void SSL_set_verify(SSL *ssl, int mode, + int (*callback)(int ok, X509_STORE_CTX *store_ctx)) { + check_ssl_x509_method(ssl); + ssl->verify_mode = mode; + if (callback != NULL) { + ssl->verify_callback = callback; + } +} + +void SSL_set_verify_depth(SSL *ssl, int depth) { + check_ssl_x509_method(ssl); + X509_VERIFY_PARAM_set_depth(ssl->param, depth); +} + +void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, + int (*cb)(X509_STORE_CTX *store_ctx, + void *arg), + void *arg) { + check_ssl_ctx_x509_method(ctx); + ctx->app_verify_callback = cb; + ctx->app_verify_arg = arg; +} + +void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, + int (*cb)(int, X509_STORE_CTX *)) { + check_ssl_ctx_x509_method(ctx); + ctx->verify_mode = mode; + ctx->default_verify_callback = cb; +} + +void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) { + check_ssl_ctx_x509_method(ctx); + X509_VERIFY_PARAM_set_depth(ctx->param, depth); +} + +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + return X509_STORE_set_default_paths(ctx->cert_store); +} + +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *ca_file, + const char *ca_dir) { + check_ssl_ctx_x509_method(ctx); + return X509_STORE_load_locations(ctx->cert_store, ca_file, ca_dir); +} + +void SSL_set_verify_result(SSL *ssl, long result) { + check_ssl_x509_method(ssl); + if (result != X509_V_OK) { + abort(); + } +} + +long SSL_get_verify_result(const SSL *ssl) { + check_ssl_x509_method(ssl); + SSL_SESSION *session = SSL_get_session(ssl); + if (session == NULL) { + return X509_V_ERR_INVALID_CALL; + } + return session->verify_result; +} + +X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + return ctx->cert_store; +} + +void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store) { + check_ssl_ctx_x509_method(ctx); + X509_STORE_free(ctx->cert_store); + ctx->cert_store = store; +} + +/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised + * contents of |x509|. */ +static CRYPTO_BUFFER *x509_to_buffer(X509 *x509) { + uint8_t *buf = NULL; + int cert_len = i2d_X509(x509, &buf); + if (cert_len <= 0) { + return 0; + } + + CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL); + OPENSSL_free(buf); + + return buffer; +} + +/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */ +static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) { + STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null(); + if (chain == NULL) { + return NULL; + } + + if (!sk_CRYPTO_BUFFER_push(chain, NULL)) { + sk_CRYPTO_BUFFER_free(chain); + return NULL; + } + + return chain; +} + +/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised + * forms of elements of |chain|. It returns one on success or zero on error, in + * which case no change to |cert->chain| is made. It preverses the existing + * leaf from |cert->chain|, if any. */ +static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) { + STACK_OF(CRYPTO_BUFFER) *new_chain = NULL; + + if (cert->chain != NULL) { + new_chain = sk_CRYPTO_BUFFER_new_null(); + if (new_chain == NULL) { + return 0; + } + + CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0); + if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) { + goto err; + } + /* |leaf| might be NULL if it's a “leafless” chain. */ + if (leaf != NULL) { + CRYPTO_BUFFER_up_ref(leaf); + } + } + + for (size_t i = 0; i < sk_X509_num(chain); i++) { + if (new_chain == NULL) { + new_chain = new_leafless_chain(); + if (new_chain == NULL) { + goto err; + } + } + + CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i)); + if (buffer == NULL || + !sk_CRYPTO_BUFFER_push(new_chain, buffer)) { + CRYPTO_BUFFER_free(buffer); + goto err; + } + } + + sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free); + cert->chain = new_chain; + + return 1; + +err: + sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free); + return 0; +} + +static void ssl_crypto_x509_cert_flush_cached_leaf(CERT *cert) { + X509_free(cert->x509_leaf); + cert->x509_leaf = NULL; +} + +static void ssl_crypto_x509_cert_flush_cached_chain(CERT *cert) { + sk_X509_pop_free(cert->x509_chain, X509_free); + cert->x509_chain = NULL; +} + +static int ssl_crypto_x509_check_client_CA_list( + STACK_OF(CRYPTO_BUFFER) *names) { + for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(names); i++) { + const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(names, i); + const uint8_t *inp = CRYPTO_BUFFER_data(buffer); + X509_NAME *name = d2i_X509_NAME(NULL, &inp, CRYPTO_BUFFER_len(buffer)); + const int ok = name != NULL && inp == CRYPTO_BUFFER_data(buffer) + + CRYPTO_BUFFER_len(buffer); + X509_NAME_free(name); + if (!ok) { + return 0; + } + } + + return 1; +} + +static void ssl_crypto_x509_cert_clear(CERT *cert) { + ssl_crypto_x509_cert_flush_cached_leaf(cert); + ssl_crypto_x509_cert_flush_cached_chain(cert); + + X509_free(cert->x509_stash); + cert->x509_stash = NULL; +} + +static void ssl_crypto_x509_cert_free(CERT *cert) { + ssl_crypto_x509_cert_clear(cert); + X509_STORE_free(cert->verify_store); +} + +static void ssl_crypto_x509_cert_dup(CERT *new_cert, const CERT *cert) { + if (cert->verify_store != NULL) { + X509_STORE_up_ref(cert->verify_store); + new_cert->verify_store = cert->verify_store; + } +} + +static int ssl_crypto_x509_session_cache_objects(SSL_SESSION *sess) { + STACK_OF(X509) *chain = NULL; + const size_t num_certs = sk_CRYPTO_BUFFER_num(sess->certs); + + if (num_certs > 0) { + chain = sk_X509_new_null(); + if (chain == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + X509 *leaf = NULL; + for (size_t i = 0; i < num_certs; i++) { + X509 *x509 = X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(sess->certs, i)); + if (x509 == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto err; + } + if (!sk_X509_push(chain, x509)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + X509_free(x509); + goto err; + } + if (i == 0) { + leaf = x509; + } + } + + sk_X509_pop_free(sess->x509_chain, X509_free); + sess->x509_chain = chain; + sk_X509_pop_free(sess->x509_chain_without_leaf, X509_free); + sess->x509_chain_without_leaf = NULL; + + X509_free(sess->x509_peer); + if (leaf != NULL) { + X509_up_ref(leaf); + } + sess->x509_peer = leaf; + + return 1; + +err: + sk_X509_pop_free(chain, X509_free); + return 0; +} + +static int ssl_crypto_x509_session_dup(SSL_SESSION *new_session, + const SSL_SESSION *session) { + if (session->x509_peer != NULL) { + X509_up_ref(session->x509_peer); + new_session->x509_peer = session->x509_peer; + } + if (session->x509_chain != NULL) { + new_session->x509_chain = X509_chain_up_ref(session->x509_chain); + if (new_session->x509_chain == NULL) { + return 0; + } + } + + return 1; +} + +static void ssl_crypto_x509_session_clear(SSL_SESSION *session) { + X509_free(session->x509_peer); + session->x509_peer = NULL; + sk_X509_pop_free(session->x509_chain, X509_free); + session->x509_chain = NULL; + sk_X509_pop_free(session->x509_chain_without_leaf, X509_free); + session->x509_chain_without_leaf = NULL; +} + +static int ssl_verify_alarm_type(long type) { + switch (type) { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + return SSL_AD_UNKNOWN_CA; + + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CRL_NOT_YET_VALID: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_CERT_REJECTED: + case X509_V_ERR_HOSTNAME_MISMATCH: + case X509_V_ERR_EMAIL_MISMATCH: + case X509_V_ERR_IP_ADDRESS_MISMATCH: + return SSL_AD_BAD_CERTIFICATE; + + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + return SSL_AD_DECRYPT_ERROR; + + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_HAS_EXPIRED: + return SSL_AD_CERTIFICATE_EXPIRED; + + case X509_V_ERR_CERT_REVOKED: + return SSL_AD_CERTIFICATE_REVOKED; + + case X509_V_ERR_UNSPECIFIED: + case X509_V_ERR_OUT_OF_MEM: + case X509_V_ERR_INVALID_CALL: + case X509_V_ERR_STORE_LOOKUP: + return SSL_AD_INTERNAL_ERROR; + + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + case X509_V_ERR_INVALID_CA: + return SSL_AD_UNKNOWN_CA; + + case X509_V_ERR_APPLICATION_VERIFICATION: + return SSL_AD_HANDSHAKE_FAILURE; + + case X509_V_ERR_INVALID_PURPOSE: + return SSL_AD_UNSUPPORTED_CERTIFICATE; + + default: + return SSL_AD_CERTIFICATE_UNKNOWN; + } +} + +static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session, + SSL *ssl) { + STACK_OF(X509) *const cert_chain = session->x509_chain; + if (cert_chain == NULL || sk_X509_num(cert_chain) == 0) { + return 0; + } + + X509_STORE *verify_store = ssl->ctx->cert_store; + if (ssl->cert->verify_store != NULL) { + verify_store = ssl->cert->verify_store; + } + + X509 *leaf = sk_X509_value(cert_chain, 0); + int ret = 0; + X509_STORE_CTX ctx; + if (!X509_STORE_CTX_init(&ctx, verify_store, leaf, cert_chain)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB); + return 0; + } + if (!X509_STORE_CTX_set_ex_data(&ctx, SSL_get_ex_data_X509_STORE_CTX_idx(), + ssl)) { + goto err; + } + + /* We need to inherit the verify parameters. These can be determined by the + * context: if its a server it will verify SSL client certificates or vice + * versa. */ + X509_STORE_CTX_set_default(&ctx, ssl->server ? "ssl_client" : "ssl_server"); + + /* Anything non-default in "param" should overwrite anything in the ctx. */ + X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(&ctx), ssl->param); + + if (ssl->verify_callback) { + X509_STORE_CTX_set_verify_cb(&ctx, ssl->verify_callback); + } + + int verify_ret; + if (ssl->ctx->app_verify_callback != NULL) { + verify_ret = ssl->ctx->app_verify_callback(&ctx, ssl->ctx->app_verify_arg); + } else { + verify_ret = X509_verify_cert(&ctx); + } + + session->verify_result = ctx.error; + + /* If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result. */ + if (verify_ret <= 0 && ssl->verify_mode != SSL_VERIFY_NONE) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, ssl_verify_alarm_type(ctx.error)); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); + goto err; + } + + ERR_clear_error(); + ret = 1; + +err: + X509_STORE_CTX_cleanup(&ctx); + return ret; +} + +static void ssl_crypto_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) { + sk_X509_NAME_pop_free(hs->cached_x509_ca_names, X509_NAME_free); + hs->cached_x509_ca_names = NULL; +} + +static int ssl_crypto_x509_ssl_new(SSL *ssl) { + ssl->param = X509_VERIFY_PARAM_new(); + if (ssl->param == NULL) { + return 0; + } + X509_VERIFY_PARAM_inherit(ssl->param, ssl->ctx->param); + return 1; +} + +static void ssl_crypto_x509_ssl_flush_cached_client_CA(SSL *ssl) { + sk_X509_NAME_pop_free(ssl->cached_x509_client_CA, X509_NAME_free); + ssl->cached_x509_client_CA = NULL; +} + +static void ssl_crypto_x509_ssl_free(SSL *ssl) { + ssl_crypto_x509_ssl_flush_cached_client_CA(ssl); + X509_VERIFY_PARAM_free(ssl->param); +} + +static int ssl_crypto_x509_ssl_auto_chain_if_needed(SSL *ssl) { + /* Only build a chain if there are no intermediates configured and the feature + * isn't disabled. */ + if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) || + !ssl_has_certificate(ssl) || + ssl->cert->chain == NULL || + sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) { + return 1; + } + + X509 *leaf = + X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0)); + if (!leaf) { + OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB); + return 0; + } + + X509_STORE_CTX ctx; + if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) { + X509_free(leaf); + OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB); + return 0; + } + + /* Attempt to build a chain, ignoring the result. */ + X509_verify_cert(&ctx); + X509_free(leaf); + ERR_clear_error(); + + /* Remove the leaf from the generated chain. */ + X509_free(sk_X509_shift(ctx.chain)); + + const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain); + X509_STORE_CTX_cleanup(&ctx); + if (!ok) { + return 0; + } + + ssl_crypto_x509_cert_flush_cached_chain(ssl->cert); + + return 1; +} + +static void ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) { + sk_X509_NAME_pop_free(ctx->cached_x509_client_CA, X509_NAME_free); + ctx->cached_x509_client_CA = NULL; +} + +static int ssl_crypto_x509_ssl_ctx_new(SSL_CTX *ctx) { + ctx->cert_store = X509_STORE_new(); + ctx->param = X509_VERIFY_PARAM_new(); + return (ctx->cert_store != NULL && ctx->param != NULL); +} + +static void ssl_crypto_x509_ssl_ctx_free(SSL_CTX *ctx) { + ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(ctx); + X509_VERIFY_PARAM_free(ctx->param); + X509_STORE_free(ctx->cert_store); +} + +const SSL_X509_METHOD ssl_crypto_x509_method = { + ssl_crypto_x509_check_client_CA_list, + ssl_crypto_x509_cert_clear, + ssl_crypto_x509_cert_free, + ssl_crypto_x509_cert_dup, + ssl_crypto_x509_cert_flush_cached_chain, + ssl_crypto_x509_cert_flush_cached_leaf, + ssl_crypto_x509_session_cache_objects, + ssl_crypto_x509_session_dup, + ssl_crypto_x509_session_clear, + ssl_crypto_x509_session_verify_cert_chain, + ssl_crypto_x509_hs_flush_cached_ca_names, + ssl_crypto_x509_ssl_new, + ssl_crypto_x509_ssl_free, + ssl_crypto_x509_ssl_flush_cached_client_CA, + ssl_crypto_x509_ssl_auto_chain_if_needed, + ssl_crypto_x509_ssl_ctx_new, + ssl_crypto_x509_ssl_ctx_free, + ssl_crypto_x509_ssl_ctx_flush_cached_client_CA, +}; + +static int ssl_use_certificate(CERT *cert, X509 *x) { + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + CRYPTO_BUFFER *buffer = x509_to_buffer(x); + if (buffer == NULL) { + return 0; + } + + const int ok = ssl_set_cert(cert, buffer); + CRYPTO_BUFFER_free(buffer); + return ok; +} + +int SSL_use_certificate(SSL *ssl, X509 *x) { + check_ssl_x509_method(ssl); + return ssl_use_certificate(ssl->cert, x); +} + +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) { + check_ssl_ctx_x509_method(ctx); + return ssl_use_certificate(ctx->cert, x); +} + +/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the + * first element of |cert->chain|. */ +static int ssl_cert_cache_leaf_cert(CERT *cert) { + assert(cert->x509_method); + + if (cert->x509_leaf != NULL || + cert->chain == NULL) { + return 1; + } + + CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0); + if (!leaf) { + return 1; + } + + cert->x509_leaf = X509_parse_from_buffer(leaf); + return cert->x509_leaf != NULL; +} + +static X509 *ssl_cert_get0_leaf(CERT *cert) { + if (cert->x509_leaf == NULL && + !ssl_cert_cache_leaf_cert(cert)) { + return NULL; + } + + return cert->x509_leaf; +} + +X509 *SSL_get_certificate(const SSL *ssl) { + check_ssl_x509_method(ssl); + return ssl_cert_get0_leaf(ssl->cert); +} + +X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock); + X509 *ret = ssl_cert_get0_leaf(ctx->cert); + CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock); + return ret; +} + +static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) { + if (!ssl_cert_set_chain(cert, chain)) { + return 0; + } + + sk_X509_pop_free(chain, X509_free); + ssl_crypto_x509_cert_flush_cached_chain(cert); + return 1; +} + +static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) { + if (!ssl_cert_set_chain(cert, chain)) { + return 0; + } + + ssl_crypto_x509_cert_flush_cached_chain(cert); + return 1; +} + +static int ssl_cert_append_cert(CERT *cert, X509 *x509) { + assert(cert->x509_method); + + CRYPTO_BUFFER *buffer = x509_to_buffer(x509); + if (buffer == NULL) { + return 0; + } + + if (cert->chain != NULL) { + if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) { + CRYPTO_BUFFER_free(buffer); + return 0; + } + + return 1; + } + + cert->chain = new_leafless_chain(); + if (cert->chain == NULL || + !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) { + CRYPTO_BUFFER_free(buffer); + sk_CRYPTO_BUFFER_free(cert->chain); + cert->chain = NULL; + return 0; + } + + return 1; +} + +static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) { + if (!ssl_cert_append_cert(cert, x509)) { + return 0; + } + + X509_free(cert->x509_stash); + cert->x509_stash = x509; + ssl_crypto_x509_cert_flush_cached_chain(cert); + return 1; +} + +static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) { + if (!ssl_cert_append_cert(cert, x509)) { + return 0; + } + + ssl_crypto_x509_cert_flush_cached_chain(cert); + return 1; +} + +int SSL_CTX_set0_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) { + check_ssl_ctx_x509_method(ctx); + return ssl_cert_set0_chain(ctx->cert, chain); +} + +int SSL_CTX_set1_chain(SSL_CTX *ctx, STACK_OF(X509) *chain) { + check_ssl_ctx_x509_method(ctx); + return ssl_cert_set1_chain(ctx->cert, chain); +} + +int SSL_set0_chain(SSL *ssl, STACK_OF(X509) *chain) { + check_ssl_x509_method(ssl); + return ssl_cert_set0_chain(ssl->cert, chain); +} + +int SSL_set1_chain(SSL *ssl, STACK_OF(X509) *chain) { + check_ssl_x509_method(ssl); + return ssl_cert_set1_chain(ssl->cert, chain); +} + +int SSL_CTX_add0_chain_cert(SSL_CTX *ctx, X509 *x509) { + check_ssl_ctx_x509_method(ctx); + return ssl_cert_add0_chain_cert(ctx->cert, x509); +} + +int SSL_CTX_add1_chain_cert(SSL_CTX *ctx, X509 *x509) { + check_ssl_ctx_x509_method(ctx); + return ssl_cert_add1_chain_cert(ctx->cert, x509); +} + +int SSL_CTX_add_extra_chain_cert(SSL_CTX *ctx, X509 *x509) { + check_ssl_ctx_x509_method(ctx); + return SSL_CTX_add0_chain_cert(ctx, x509); +} + +int SSL_add0_chain_cert(SSL *ssl, X509 *x509) { + check_ssl_x509_method(ssl); + return ssl_cert_add0_chain_cert(ssl->cert, x509); +} + +int SSL_add1_chain_cert(SSL *ssl, X509 *x509) { + check_ssl_x509_method(ssl); + return ssl_cert_add1_chain_cert(ssl->cert, x509); +} + +int SSL_CTX_clear_chain_certs(SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + return SSL_CTX_set0_chain(ctx, NULL); +} + +int SSL_CTX_clear_extra_chain_certs(SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + return SSL_CTX_clear_chain_certs(ctx); +} + +int SSL_clear_chain_certs(SSL *ssl) { + check_ssl_x509_method(ssl); + return SSL_set0_chain(ssl, NULL); +} + +/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of + * |cert->chain|. */ +static int ssl_cert_cache_chain_certs(CERT *cert) { + assert(cert->x509_method); + + if (cert->x509_chain != NULL || + cert->chain == NULL || + sk_CRYPTO_BUFFER_num(cert->chain) < 2) { + return 1; + } + + STACK_OF(X509) *chain = sk_X509_new_null(); + if (chain == NULL) { + return 0; + } + + for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) { + CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i); + X509 *x509 = X509_parse_from_buffer(buffer); + if (x509 == NULL || + !sk_X509_push(chain, x509)) { + X509_free(x509); + goto err; + } + } + + cert->x509_chain = chain; + return 1; + +err: + sk_X509_pop_free(chain, X509_free); + return 0; +} + +int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) { + check_ssl_ctx_x509_method(ctx); + CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock); + const int ret = ssl_cert_cache_chain_certs(ctx->cert); + CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock); + + if (!ret) { + *out_chain = NULL; + return 0; + } + + *out_chain = ctx->cert->x509_chain; + return 1; +} + +int SSL_CTX_get_extra_chain_certs(const SSL_CTX *ctx, + STACK_OF(X509) **out_chain) { + return SSL_CTX_get0_chain_certs(ctx, out_chain); +} + +int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) { + check_ssl_x509_method(ssl); + if (!ssl_cert_cache_chain_certs(ssl->cert)) { + *out_chain = NULL; + return 0; + } + + *out_chain = ssl->cert->x509_chain; + return 1; +} + +static SSL_SESSION *ssl_session_new_with_crypto_x509(void) { + return ssl_session_new(&ssl_crypto_x509_method); +} + +SSL_SESSION *d2i_SSL_SESSION_bio(BIO *bio, SSL_SESSION **out) { + return ASN1_d2i_bio_of(SSL_SESSION, ssl_session_new_with_crypto_x509, + d2i_SSL_SESSION, bio, out); +} + +int i2d_SSL_SESSION_bio(BIO *bio, const SSL_SESSION *session) { + return ASN1_i2d_bio_of(SSL_SESSION, i2d_SSL_SESSION, bio, session); +} + +IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION) + +SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { + if (length < 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return NULL; + } + + CBS cbs; + CBS_init(&cbs, *pp, length); + + SSL_SESSION *ret = SSL_SESSION_parse(&cbs, &ssl_crypto_x509_method, + NULL /* no buffer pool */); + if (ret == NULL) { + return NULL; + } + + if (a) { + SSL_SESSION_free(*a); + *a = ret; + } + *pp = CBS_data(&cbs); + return ret; +} + +STACK_OF(X509_NAME) *SSL_dup_CA_list(STACK_OF(X509_NAME) *list) { + return sk_X509_NAME_deep_copy(list, X509_NAME_dup, X509_NAME_free); +} + +static void set_client_CA_list(STACK_OF(CRYPTO_BUFFER) **ca_list, + const STACK_OF(X509_NAME) *name_list, + CRYPTO_BUFFER_POOL *pool) { + STACK_OF(CRYPTO_BUFFER) *buffers = sk_CRYPTO_BUFFER_new_null(); + if (buffers == NULL) { + return; + } + + for (size_t i = 0; i < sk_X509_NAME_num(name_list); i++) { + X509_NAME *name = sk_X509_NAME_value(name_list, i); + uint8_t *outp = NULL; + int len = i2d_X509_NAME(name, &outp); + if (len < 0) { + goto err; + } + + CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(outp, len, pool); + OPENSSL_free(outp); + if (buffer == NULL || + !sk_CRYPTO_BUFFER_push(buffers, buffer)) { + CRYPTO_BUFFER_free(buffer); + goto err; + } + } + + sk_CRYPTO_BUFFER_pop_free(*ca_list, CRYPTO_BUFFER_free); + *ca_list = buffers; + return; + +err: + sk_CRYPTO_BUFFER_pop_free(buffers, CRYPTO_BUFFER_free); +} + +void SSL_set_client_CA_list(SSL *ssl, STACK_OF(X509_NAME) *name_list) { + check_ssl_x509_method(ssl); + ssl->ctx->x509_method->ssl_flush_cached_client_CA(ssl); + set_client_CA_list(&ssl->client_CA, name_list, ssl->ctx->pool); + sk_X509_NAME_pop_free(name_list, X509_NAME_free); +} + +void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) *name_list) { + check_ssl_ctx_x509_method(ctx); + ctx->x509_method->ssl_ctx_flush_cached_client_CA(ctx); + set_client_CA_list(&ctx->client_CA, name_list, ctx->pool); + sk_X509_NAME_pop_free(name_list, X509_NAME_free); +} + +static STACK_OF(X509_NAME) * + buffer_names_to_x509(const STACK_OF(CRYPTO_BUFFER) *names, + STACK_OF(X509_NAME) **cached) { + if (names == NULL) { + return NULL; + } + + if (*cached != NULL) { + return *cached; + } + + STACK_OF(X509_NAME) *new_cache = sk_X509_NAME_new_null(); + if (new_cache == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return NULL; + } + + for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(names); i++) { + const CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(names, i); + const uint8_t *inp = CRYPTO_BUFFER_data(buffer); + X509_NAME *name = d2i_X509_NAME(NULL, &inp, CRYPTO_BUFFER_len(buffer)); + if (name == NULL || + inp != CRYPTO_BUFFER_data(buffer) + CRYPTO_BUFFER_len(buffer) || + !sk_X509_NAME_push(new_cache, name)) { + X509_NAME_free(name); + goto err; + } + } + + *cached = new_cache; + return new_cache; + +err: + sk_X509_NAME_pop_free(new_cache, X509_NAME_free); + return NULL; +} + +STACK_OF(X509_NAME) *SSL_get_client_CA_list(const SSL *ssl) { + check_ssl_x509_method(ssl); + /* For historical reasons, this function is used both to query configuration + * state on a server as well as handshake state on a client. However, whether + * |ssl| is a client or server is not known until explicitly configured with + * |SSL_set_connect_state|. If |handshake_func| is NULL, |ssl| is in an + * indeterminate mode and |ssl->server| is unset. */ + if (ssl->handshake_func != NULL && !ssl->server) { + if (ssl->s3->hs != NULL) { + return buffer_names_to_x509(ssl->s3->hs->ca_names, + &ssl->s3->hs->cached_x509_ca_names); + } + + return NULL; + } + + if (ssl->client_CA != NULL) { + return buffer_names_to_x509( + ssl->client_CA, (STACK_OF(X509_NAME) **)&ssl->cached_x509_client_CA); + } + return buffer_names_to_x509(ssl->ctx->client_CA, + &ssl->ctx->cached_x509_client_CA); +} + +STACK_OF(X509_NAME) *SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) { + check_ssl_ctx_x509_method(ctx); + CRYPTO_MUTEX_lock_write((CRYPTO_MUTEX *) &ctx->lock); + STACK_OF(X509_NAME) *ret = buffer_names_to_x509( + ctx->client_CA, (STACK_OF(X509_NAME) **)&ctx->cached_x509_client_CA); + CRYPTO_MUTEX_unlock_write((CRYPTO_MUTEX *) &ctx->lock); + return ret; +} + +static int add_client_CA(STACK_OF(CRYPTO_BUFFER) **names, X509 *x509, + CRYPTO_BUFFER_POOL *pool) { + if (x509 == NULL) { + return 0; + } + + uint8_t *outp = NULL; + int len = i2d_X509_NAME(X509_get_subject_name(x509), &outp); + if (len < 0) { + return 0; + } + + CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(outp, len, pool); + OPENSSL_free(outp); + if (buffer == NULL) { + return 0; + } + + int alloced = 0; + if (*names == NULL) { + *names = sk_CRYPTO_BUFFER_new_null(); + alloced = 1; + + if (*names == NULL) { + CRYPTO_BUFFER_free(buffer); + return 0; + } + } + + if (!sk_CRYPTO_BUFFER_push(*names, buffer)) { + CRYPTO_BUFFER_free(buffer); + if (alloced) { + sk_CRYPTO_BUFFER_pop_free(*names, CRYPTO_BUFFER_free); + *names = NULL; + } + return 0; + } + + return 1; +} + +int SSL_add_client_CA(SSL *ssl, X509 *x509) { + check_ssl_x509_method(ssl); + if (!add_client_CA(&ssl->client_CA, x509, ssl->ctx->pool)) { + return 0; + } + + ssl_crypto_x509_ssl_flush_cached_client_CA(ssl); + return 1; +} + +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x509) { + check_ssl_ctx_x509_method(ctx); + if (!add_client_CA(&ctx->client_CA, x509, ctx->pool)) { + return 0; + } + + ssl_crypto_x509_ssl_ctx_flush_cached_client_CA(ctx); + return 1; +} + +static int do_client_cert_cb(SSL *ssl, void *arg) { + if (ssl_has_certificate(ssl) || ssl->ctx->client_cert_cb == NULL) { + return 1; + } + + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + int ret = ssl->ctx->client_cert_cb(ssl, &x509, &pkey); + if (ret < 0) { + return -1; + } + + if (ret != 0) { + if (!SSL_use_certificate(ssl, x509) || + !SSL_use_PrivateKey(ssl, pkey)) { + return 0; + } + } + + X509_free(x509); + EVP_PKEY_free(pkey); + return 1; +} + +void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, + X509 **out_x509, + EVP_PKEY **out_pkey)) { + check_ssl_ctx_x509_method(ctx); + /* Emulate the old client certificate callback with the new one. */ + SSL_CTX_set_cert_cb(ctx, do_client_cert_cb, NULL); + ctx->client_cert_cb = cb; +} + +static int set_cert_store(X509_STORE **store_ptr, X509_STORE *new_store, + int take_ref) { + X509_STORE_free(*store_ptr); + *store_ptr = new_store; + + if (new_store != NULL && take_ref) { + X509_STORE_up_ref(new_store); + } + + return 1; +} + +int SSL_get_ex_data_X509_STORE_CTX_idx(void) { + /* The ex_data index to go from |X509_STORE_CTX| to |SSL| always uses the + * reserved app_data slot. Before ex_data was introduced, app_data was used. + * Avoid breaking any software which assumes |X509_STORE_CTX_get_app_data| + * works. */ + return 0; +} + +int SSL_CTX_set0_verify_cert_store(SSL_CTX *ctx, X509_STORE *store) { + check_ssl_ctx_x509_method(ctx); + return set_cert_store(&ctx->cert->verify_store, store, 0); +} + +int SSL_CTX_set1_verify_cert_store(SSL_CTX *ctx, X509_STORE *store) { + check_ssl_ctx_x509_method(ctx); + return set_cert_store(&ctx->cert->verify_store, store, 1); +} + +int SSL_set0_verify_cert_store(SSL *ssl, X509_STORE *store) { + check_ssl_x509_method(ssl); + return set_cert_store(&ssl->cert->verify_store, store, 0); +} + +int SSL_set1_verify_cert_store(SSL *ssl, X509_STORE *store) { + check_ssl_x509_method(ssl); + return set_cert_store(&ssl->cert->verify_store, store, 1); +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/t1_enc.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/t1_enc.c new file mode 100644 index 000000000..9f11e0566 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/t1_enc.c @@ -0,0 +1,561 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ +/* ==================================================================== + * Copyright 2005 Nokia. All rights reserved. + * + * The portions of the attached software ("Contribution") is developed by + * Nokia Corporation and is licensed pursuant to the OpenSSL open source + * license. + * + * The Contribution, originally written by Mika Kousa and Pasi Eronen of + * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites + * support (see RFC 4279) to OpenSSL. + * + * No patent licenses or other rights except those expressly stated in + * the OpenSSL open source license shall be deemed granted or received + * expressly, by implication, estoppel, or otherwise. + * + * No assurances are provided by Nokia that the Contribution does not + * infringe the patent or other intellectual property rights of any third + * party or that the license provides you with all the necessary rights + * to make use of the Contribution. + * + * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN + * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA + * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY + * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR + * OTHERWISE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/nid.h> +#include <openssl/rand.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +/* tls1_P_hash computes the TLS P_<hash> function as described in RFC 5246, + * section 5. It XORs |out_len| bytes to |out|, using |md| as the hash and + * |secret| as the secret. |seed1| through |seed3| are concatenated to form the + * seed parameter. It returns one on success and zero on failure. */ +static int tls1_P_hash(uint8_t *out, size_t out_len, const EVP_MD *md, + const uint8_t *secret, size_t secret_len, + const uint8_t *seed1, size_t seed1_len, + const uint8_t *seed2, size_t seed2_len, + const uint8_t *seed3, size_t seed3_len) { + HMAC_CTX ctx, ctx_tmp, ctx_init; + uint8_t A1[EVP_MAX_MD_SIZE]; + unsigned A1_len; + int ret = 0; + + size_t chunk = EVP_MD_size(md); + + HMAC_CTX_init(&ctx); + HMAC_CTX_init(&ctx_tmp); + HMAC_CTX_init(&ctx_init); + if (!HMAC_Init_ex(&ctx_init, secret, secret_len, md, NULL) || + !HMAC_CTX_copy_ex(&ctx, &ctx_init) || + !HMAC_Update(&ctx, seed1, seed1_len) || + !HMAC_Update(&ctx, seed2, seed2_len) || + !HMAC_Update(&ctx, seed3, seed3_len) || + !HMAC_Final(&ctx, A1, &A1_len)) { + goto err; + } + + for (;;) { + unsigned len; + uint8_t hmac[EVP_MAX_MD_SIZE]; + if (!HMAC_CTX_copy_ex(&ctx, &ctx_init) || + !HMAC_Update(&ctx, A1, A1_len) || + /* Save a copy of |ctx| to compute the next A1 value below. */ + (out_len > chunk && !HMAC_CTX_copy_ex(&ctx_tmp, &ctx)) || + !HMAC_Update(&ctx, seed1, seed1_len) || + !HMAC_Update(&ctx, seed2, seed2_len) || + !HMAC_Update(&ctx, seed3, seed3_len) || + !HMAC_Final(&ctx, hmac, &len)) { + goto err; + } + assert(len == chunk); + + /* XOR the result into |out|. */ + if (len > out_len) { + len = out_len; + } + unsigned i; + for (i = 0; i < len; i++) { + out[i] ^= hmac[i]; + } + out += len; + out_len -= len; + + if (out_len == 0) { + break; + } + + /* Calculate the next A1 value. */ + if (!HMAC_Final(&ctx_tmp, A1, &A1_len)) { + goto err; + } + } + + ret = 1; + +err: + HMAC_CTX_cleanup(&ctx); + HMAC_CTX_cleanup(&ctx_tmp); + HMAC_CTX_cleanup(&ctx_init); + OPENSSL_cleanse(A1, sizeof(A1)); + return ret; +} + +int tls1_prf(const EVP_MD *digest, uint8_t *out, size_t out_len, + const uint8_t *secret, size_t secret_len, const char *label, + size_t label_len, const uint8_t *seed1, size_t seed1_len, + const uint8_t *seed2, size_t seed2_len) { + if (out_len == 0) { + return 1; + } + + OPENSSL_memset(out, 0, out_len); + + if (digest == EVP_md5_sha1()) { + /* If using the MD5/SHA1 PRF, |secret| is partitioned between SHA-1 and + * MD5, MD5 first. */ + size_t secret_half = secret_len - (secret_len / 2); + if (!tls1_P_hash(out, out_len, EVP_md5(), secret, secret_half, + (const uint8_t *)label, label_len, seed1, seed1_len, seed2, + seed2_len)) { + return 0; + } + + /* Note that, if |secret_len| is odd, the two halves share a byte. */ + secret = secret + (secret_len - secret_half); + secret_len = secret_half; + + digest = EVP_sha1(); + } + + if (!tls1_P_hash(out, out_len, digest, secret, secret_len, + (const uint8_t *)label, label_len, seed1, seed1_len, seed2, + seed2_len)) { + return 0; + } + + return 1; +} + +static int ssl3_prf(uint8_t *out, size_t out_len, const uint8_t *secret, + size_t secret_len, const char *label, size_t label_len, + const uint8_t *seed1, size_t seed1_len, + const uint8_t *seed2, size_t seed2_len) { + EVP_MD_CTX md5; + EVP_MD_CTX sha1; + uint8_t buf[16], smd[SHA_DIGEST_LENGTH]; + uint8_t c = 'A'; + size_t i, j, k; + + k = 0; + EVP_MD_CTX_init(&md5); + EVP_MD_CTX_init(&sha1); + for (i = 0; i < out_len; i += MD5_DIGEST_LENGTH) { + k++; + if (k > sizeof(buf)) { + /* bug: 'buf' is too small for this ciphersuite */ + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + for (j = 0; j < k; j++) { + buf[j] = c; + } + c++; + if (!EVP_DigestInit_ex(&sha1, EVP_sha1(), NULL)) { + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); + return 0; + } + EVP_DigestUpdate(&sha1, buf, k); + EVP_DigestUpdate(&sha1, secret, secret_len); + /* |label| is ignored for SSLv3. */ + if (seed1_len) { + EVP_DigestUpdate(&sha1, seed1, seed1_len); + } + if (seed2_len) { + EVP_DigestUpdate(&sha1, seed2, seed2_len); + } + EVP_DigestFinal_ex(&sha1, smd, NULL); + + if (!EVP_DigestInit_ex(&md5, EVP_md5(), NULL)) { + OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP); + return 0; + } + EVP_DigestUpdate(&md5, secret, secret_len); + EVP_DigestUpdate(&md5, smd, SHA_DIGEST_LENGTH); + if (i + MD5_DIGEST_LENGTH > out_len) { + EVP_DigestFinal_ex(&md5, smd, NULL); + OPENSSL_memcpy(out, smd, out_len - i); + } else { + EVP_DigestFinal_ex(&md5, out, NULL); + } + + out += MD5_DIGEST_LENGTH; + } + + OPENSSL_cleanse(smd, SHA_DIGEST_LENGTH); + EVP_MD_CTX_cleanup(&md5); + EVP_MD_CTX_cleanup(&sha1); + + return 1; +} + +static int tls1_setup_key_block(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (hs->key_block_len != 0) { + return 1; + } + + SSL_SESSION *session = ssl->session; + if (hs->new_session != NULL) { + session = hs->new_session; + } + + const EVP_AEAD *aead = NULL; + size_t mac_secret_len, fixed_iv_len; + if (session->cipher == NULL || + !ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len, + session->cipher, ssl3_protocol_version(ssl))) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE); + return 0; + } + size_t key_len = EVP_AEAD_key_length(aead); + if (mac_secret_len > 0) { + /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher suites) the + * key length reported by |EVP_AEAD_key_length| will include the MAC key + * bytes and initial implicit IV. */ + if (key_len < mac_secret_len + fixed_iv_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + key_len -= mac_secret_len + fixed_iv_len; + } + + assert(mac_secret_len < 256); + assert(key_len < 256); + assert(fixed_iv_len < 256); + + ssl->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len; + ssl->s3->tmp.new_key_len = (uint8_t)key_len; + ssl->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len; + + size_t key_block_len = SSL_get_key_block_len(ssl); + + uint8_t *keyblock = OPENSSL_malloc(key_block_len); + if (keyblock == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + + if (!SSL_generate_key_block(ssl, keyblock, key_block_len)) { + OPENSSL_free(keyblock); + return 0; + } + + assert(key_block_len < 256); + hs->key_block_len = (uint8_t)key_block_len; + hs->key_block = keyblock; + return 1; +} + +int tls1_change_cipher_state(SSL_HANDSHAKE *hs, int which) { + SSL *const ssl = hs->ssl; + /* Ensure the key block is set up. */ + if (!tls1_setup_key_block(hs)) { + return 0; + } + + /* is_read is true if we have just read a ChangeCipherSpec message - i.e. we + * need to update the read cipherspec. Otherwise we have just written one. */ + const char is_read = (which & SSL3_CC_READ) != 0; + /* use_client_keys is true if we wish to use the keys for the "client write" + * direction. This is the case if we're a client sending a ChangeCipherSpec, + * or a server reading a client's ChangeCipherSpec. */ + const char use_client_keys = which == SSL3_CHANGE_CIPHER_CLIENT_WRITE || + which == SSL3_CHANGE_CIPHER_SERVER_READ; + + size_t mac_secret_len = ssl->s3->tmp.new_mac_secret_len; + size_t key_len = ssl->s3->tmp.new_key_len; + size_t iv_len = ssl->s3->tmp.new_fixed_iv_len; + assert((mac_secret_len + key_len + iv_len) * 2 == hs->key_block_len); + + const uint8_t *key_data = hs->key_block; + const uint8_t *client_write_mac_secret = key_data; + key_data += mac_secret_len; + const uint8_t *server_write_mac_secret = key_data; + key_data += mac_secret_len; + const uint8_t *client_write_key = key_data; + key_data += key_len; + const uint8_t *server_write_key = key_data; + key_data += key_len; + const uint8_t *client_write_iv = key_data; + key_data += iv_len; + const uint8_t *server_write_iv = key_data; + key_data += iv_len; + + const uint8_t *mac_secret, *key, *iv; + if (use_client_keys) { + mac_secret = client_write_mac_secret; + key = client_write_key; + iv = client_write_iv; + } else { + mac_secret = server_write_mac_secret; + key = server_write_key; + iv = server_write_iv; + } + + SSL_AEAD_CTX *aead_ctx = SSL_AEAD_CTX_new( + is_read ? evp_aead_open : evp_aead_seal, ssl3_protocol_version(ssl), + hs->new_cipher, key, key_len, mac_secret, mac_secret_len, iv, iv_len); + if (aead_ctx == NULL) { + return 0; + } + + if (is_read) { + return ssl->method->set_read_state(ssl, aead_ctx); + } + + return ssl->method->set_write_state(ssl, aead_ctx); +} + +size_t SSL_get_key_block_len(const SSL *ssl) { + return 2 * ((size_t)ssl->s3->tmp.new_mac_secret_len + + (size_t)ssl->s3->tmp.new_key_len + + (size_t)ssl->s3->tmp.new_fixed_iv_len); +} + +int SSL_generate_key_block(const SSL *ssl, uint8_t *out, size_t out_len) { + if (ssl3_protocol_version(ssl) == SSL3_VERSION) { + return ssl3_prf(out, out_len, SSL_get_session(ssl)->master_key, + SSL_get_session(ssl)->master_key_length, + TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE, + ssl->s3->server_random, SSL3_RANDOM_SIZE, + ssl->s3->client_random, SSL3_RANDOM_SIZE); + } + + const EVP_MD *digest = ssl_get_handshake_digest( + SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl)); + if (digest == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + return tls1_prf(digest, out, out_len, SSL_get_session(ssl)->master_key, + SSL_get_session(ssl)->master_key_length, + TLS_MD_KEY_EXPANSION_CONST, TLS_MD_KEY_EXPANSION_CONST_SIZE, + ssl->s3->server_random, SSL3_RANDOM_SIZE, + ssl->s3->client_random, SSL3_RANDOM_SIZE); +} + +int tls1_generate_master_secret(SSL_HANDSHAKE *hs, uint8_t *out, + const uint8_t *premaster, + size_t premaster_len) { + const SSL *ssl = hs->ssl; + if (hs->extended_master_secret) { + uint8_t digests[EVP_MAX_MD_SIZE]; + size_t digests_len; + if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, digests, &digests_len) || + !tls1_prf(SSL_TRANSCRIPT_md(&hs->transcript), out, + SSL3_MASTER_SECRET_SIZE, premaster, premaster_len, + TLS_MD_EXTENDED_MASTER_SECRET_CONST, + TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE, digests, + digests_len, NULL, 0)) { + return 0; + } + } else { + if (ssl3_protocol_version(ssl) == SSL3_VERSION) { + if (!ssl3_prf(out, SSL3_MASTER_SECRET_SIZE, premaster, premaster_len, + TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE, + ssl->s3->client_random, SSL3_RANDOM_SIZE, + ssl->s3->server_random, SSL3_RANDOM_SIZE)) { + return 0; + } + } else { + if (!tls1_prf(SSL_TRANSCRIPT_md(&hs->transcript), out, + SSL3_MASTER_SECRET_SIZE, premaster, premaster_len, + TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE, + ssl->s3->client_random, SSL3_RANDOM_SIZE, + ssl->s3->server_random, SSL3_RANDOM_SIZE)) { + return 0; + } + } + } + + return SSL3_MASTER_SECRET_SIZE; +} + +int SSL_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len, + const char *label, size_t label_len, + const uint8_t *context, size_t context_len, + int use_context) { + if (!ssl->s3->have_version || ssl->version == SSL3_VERSION) { + return 0; + } + + /* Exporters may not be used in the middle of a renegotiation. */ + if (SSL_in_init(ssl) && !SSL_in_false_start(ssl)) { + return 0; + } + + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return tls13_export_keying_material(ssl, out, out_len, label, label_len, + context, context_len, use_context); + } + + size_t seed_len = 2 * SSL3_RANDOM_SIZE; + if (use_context) { + if (context_len >= 1u << 16) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + seed_len += 2 + context_len; + } + uint8_t *seed = OPENSSL_malloc(seed_len); + if (seed == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return 0; + } + + OPENSSL_memcpy(seed, ssl->s3->client_random, SSL3_RANDOM_SIZE); + OPENSSL_memcpy(seed + SSL3_RANDOM_SIZE, ssl->s3->server_random, + SSL3_RANDOM_SIZE); + if (use_context) { + seed[2 * SSL3_RANDOM_SIZE] = (uint8_t)(context_len >> 8); + seed[2 * SSL3_RANDOM_SIZE + 1] = (uint8_t)context_len; + OPENSSL_memcpy(seed + 2 * SSL3_RANDOM_SIZE + 2, context, context_len); + } + + const EVP_MD *digest = ssl_get_handshake_digest( + SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl)); + if (digest == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + int ret = tls1_prf(digest, out, out_len, SSL_get_session(ssl)->master_key, + SSL_get_session(ssl)->master_key_length, label, label_len, + seed, seed_len, NULL, 0); + OPENSSL_free(seed); + return ret; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/t1_lib.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/t1_lib.c new file mode 100644 index 000000000..759d87bfd --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/t1_lib.c @@ -0,0 +1,3562 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/digest.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/mem.h> +#include <openssl/nid.h> +#include <openssl/rand.h> +#include <openssl/type_check.h> + +#include "internal.h" +#include "../crypto/internal.h" + + +static int ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs); + +static int compare_uint16_t(const void *p1, const void *p2) { + uint16_t u1 = *((const uint16_t *)p1); + uint16_t u2 = *((const uint16_t *)p2); + if (u1 < u2) { + return -1; + } else if (u1 > u2) { + return 1; + } else { + return 0; + } +} + +/* Per http://tools.ietf.org/html/rfc5246#section-7.4.1.4, there may not be + * more than one extension of the same type in a ClientHello or ServerHello. + * This function does an initial scan over the extensions block to filter those + * out. */ +static int tls1_check_duplicate_extensions(const CBS *cbs) { + CBS extensions = *cbs; + size_t num_extensions = 0, i = 0; + uint16_t *extension_types = NULL; + int ret = 0; + + /* First pass: count the extensions. */ + while (CBS_len(&extensions) > 0) { + uint16_t type; + CBS extension; + + if (!CBS_get_u16(&extensions, &type) || + !CBS_get_u16_length_prefixed(&extensions, &extension)) { + goto done; + } + + num_extensions++; + } + + if (num_extensions == 0) { + return 1; + } + + extension_types = OPENSSL_malloc(sizeof(uint16_t) * num_extensions); + if (extension_types == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto done; + } + + /* Second pass: gather the extension types. */ + extensions = *cbs; + for (i = 0; i < num_extensions; i++) { + CBS extension; + + if (!CBS_get_u16(&extensions, &extension_types[i]) || + !CBS_get_u16_length_prefixed(&extensions, &extension)) { + /* This should not happen. */ + goto done; + } + } + assert(CBS_len(&extensions) == 0); + + /* Sort the extensions and make sure there are no duplicates. */ + qsort(extension_types, num_extensions, sizeof(uint16_t), compare_uint16_t); + for (i = 1; i < num_extensions; i++) { + if (extension_types[i - 1] == extension_types[i]) { + goto done; + } + } + + ret = 1; + +done: + OPENSSL_free(extension_types); + return ret; +} + +int ssl_client_hello_init(SSL *ssl, SSL_CLIENT_HELLO *out, const uint8_t *in, + size_t in_len) { + OPENSSL_memset(out, 0, sizeof(*out)); + out->ssl = ssl; + out->client_hello = in; + out->client_hello_len = in_len; + + CBS client_hello, random, session_id; + CBS_init(&client_hello, out->client_hello, out->client_hello_len); + if (!CBS_get_u16(&client_hello, &out->version) || + !CBS_get_bytes(&client_hello, &random, SSL3_RANDOM_SIZE) || + !CBS_get_u8_length_prefixed(&client_hello, &session_id) || + CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) { + return 0; + } + + out->random = CBS_data(&random); + out->random_len = CBS_len(&random); + out->session_id = CBS_data(&session_id); + out->session_id_len = CBS_len(&session_id); + + /* Skip past DTLS cookie */ + if (SSL_is_dtls(out->ssl)) { + CBS cookie; + if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) || + CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) { + return 0; + } + } + + CBS cipher_suites, compression_methods; + if (!CBS_get_u16_length_prefixed(&client_hello, &cipher_suites) || + CBS_len(&cipher_suites) < 2 || (CBS_len(&cipher_suites) & 1) != 0 || + !CBS_get_u8_length_prefixed(&client_hello, &compression_methods) || + CBS_len(&compression_methods) < 1) { + return 0; + } + + out->cipher_suites = CBS_data(&cipher_suites); + out->cipher_suites_len = CBS_len(&cipher_suites); + out->compression_methods = CBS_data(&compression_methods); + out->compression_methods_len = CBS_len(&compression_methods); + + /* If the ClientHello ends here then it's valid, but doesn't have any + * extensions. (E.g. SSLv3.) */ + if (CBS_len(&client_hello) == 0) { + out->extensions = NULL; + out->extensions_len = 0; + return 1; + } + + /* Extract extensions and check it is valid. */ + CBS extensions; + if (!CBS_get_u16_length_prefixed(&client_hello, &extensions) || + !tls1_check_duplicate_extensions(&extensions) || + CBS_len(&client_hello) != 0) { + return 0; + } + + out->extensions = CBS_data(&extensions); + out->extensions_len = CBS_len(&extensions); + + return 1; +} + +int ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello, + CBS *out, uint16_t extension_type) { + CBS extensions; + CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len); + while (CBS_len(&extensions) != 0) { + /* Decode the next extension. */ + uint16_t type; + CBS extension; + if (!CBS_get_u16(&extensions, &type) || + !CBS_get_u16_length_prefixed(&extensions, &extension)) { + return 0; + } + + if (type == extension_type) { + *out = extension; + return 1; + } + } + + return 0; +} + +int SSL_early_callback_ctx_extension_get(const SSL_CLIENT_HELLO *client_hello, + uint16_t extension_type, + const uint8_t **out_data, + size_t *out_len) { + CBS cbs; + if (!ssl_client_hello_get_extension(client_hello, &cbs, extension_type)) { + return 0; + } + + *out_data = CBS_data(&cbs); + *out_len = CBS_len(&cbs); + return 1; +} + +static const uint16_t kDefaultGroups[] = { + SSL_CURVE_X25519, + SSL_CURVE_SECP256R1, + SSL_CURVE_SECP384R1, +}; + +void tls1_get_grouplist(SSL *ssl, const uint16_t **out_group_ids, + size_t *out_group_ids_len) { + *out_group_ids = ssl->supported_group_list; + *out_group_ids_len = ssl->supported_group_list_len; + if (!*out_group_ids) { + *out_group_ids = kDefaultGroups; + *out_group_ids_len = OPENSSL_ARRAY_SIZE(kDefaultGroups); + } +} + +int tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id) { + SSL *const ssl = hs->ssl; + assert(ssl->server); + + const uint16_t *groups, *pref, *supp; + size_t groups_len, pref_len, supp_len; + tls1_get_grouplist(ssl, &groups, &groups_len); + + /* Clients are not required to send a supported_groups extension. In this + * case, the server is free to pick any group it likes. See RFC 4492, + * section 4, paragraph 3. + * + * However, in the interests of compatibility, we will skip ECDH if the + * client didn't send an extension because we can't be sure that they'll + * support our favoured group. Thus we do not special-case an emtpy + * |peer_supported_group_list|. */ + + if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) { + pref = groups; + pref_len = groups_len; + supp = hs->peer_supported_group_list; + supp_len = hs->peer_supported_group_list_len; + } else { + pref = hs->peer_supported_group_list; + pref_len = hs->peer_supported_group_list_len; + supp = groups; + supp_len = groups_len; + } + + for (size_t i = 0; i < pref_len; i++) { + for (size_t j = 0; j < supp_len; j++) { + if (pref[i] == supp[j]) { + *out_group_id = pref[i]; + return 1; + } + } + } + + return 0; +} + +int tls1_set_curves(uint16_t **out_group_ids, size_t *out_group_ids_len, + const int *curves, size_t ncurves) { + uint16_t *group_ids; + + group_ids = OPENSSL_malloc(ncurves * sizeof(uint16_t)); + if (group_ids == NULL) { + return 0; + } + + for (size_t i = 0; i < ncurves; i++) { + if (!ssl_nid_to_group_id(&group_ids[i], curves[i])) { + OPENSSL_free(group_ids); + return 0; + } + } + + OPENSSL_free(*out_group_ids); + *out_group_ids = group_ids; + *out_group_ids_len = ncurves; + + return 1; +} + +int tls1_set_curves_list(uint16_t **out_group_ids, size_t *out_group_ids_len, + const char *curves) { + uint16_t *group_ids = NULL; + size_t ncurves = 0; + + const char *col; + const char *ptr = curves; + + do { + col = strchr(ptr, ':'); + + uint16_t group_id; + if (!ssl_name_to_group_id(&group_id, ptr, + col ? (size_t)(col - ptr) : strlen(ptr))) { + goto err; + } + + uint16_t *new_group_ids = OPENSSL_realloc(group_ids, + (ncurves + 1) * sizeof(uint16_t)); + if (new_group_ids == NULL) { + goto err; + } + group_ids = new_group_ids; + + group_ids[ncurves] = group_id; + ncurves++; + + if (col) { + ptr = col + 1; + } + } while (col); + + OPENSSL_free(*out_group_ids); + *out_group_ids = group_ids; + *out_group_ids_len = ncurves; + + return 1; + +err: + OPENSSL_free(group_ids); + return 0; +} + +int tls1_check_group_id(SSL *ssl, uint16_t group_id) { + const uint16_t *groups; + size_t groups_len; + tls1_get_grouplist(ssl, &groups, &groups_len); + for (size_t i = 0; i < groups_len; i++) { + if (groups[i] == group_id) { + return 1; + } + } + + return 0; +} + +/* kVerifySignatureAlgorithms is the default list of accepted signature + * algorithms for verifying. + * + * For now, RSA-PSS signature algorithms are not enabled on Android's system + * BoringSSL. Once the change in Chrome has stuck and the values are finalized, + * restore them. */ +static const uint16_t kVerifySignatureAlgorithms[] = { + /* Prefer SHA-256 algorithms. */ + SSL_SIGN_ECDSA_SECP256R1_SHA256, +#if !defined(BORINGSSL_ANDROID_SYSTEM) + SSL_SIGN_RSA_PSS_SHA256, +#endif + SSL_SIGN_RSA_PKCS1_SHA256, + + /* Larger hashes are acceptable. */ + SSL_SIGN_ECDSA_SECP384R1_SHA384, +#if !defined(BORINGSSL_ANDROID_SYSTEM) + SSL_SIGN_RSA_PSS_SHA384, +#endif + SSL_SIGN_RSA_PKCS1_SHA384, + + /* TODO(davidben): Remove this. */ +#if defined(BORINGSSL_ANDROID_SYSTEM) + SSL_SIGN_ECDSA_SECP521R1_SHA512, +#endif +#if !defined(BORINGSSL_ANDROID_SYSTEM) + SSL_SIGN_RSA_PSS_SHA512, +#endif + SSL_SIGN_RSA_PKCS1_SHA512, + + /* For now, SHA-1 is still accepted but least preferable. */ + SSL_SIGN_RSA_PKCS1_SHA1, + +}; + +/* kSignSignatureAlgorithms is the default list of supported signature + * algorithms for signing. + * + * For now, RSA-PSS signature algorithms are not enabled on Android's system + * BoringSSL. Once the change in Chrome has stuck and the values are finalized, + * restore them. */ +static const uint16_t kSignSignatureAlgorithms[] = { + /* Prefer SHA-256 algorithms. */ + SSL_SIGN_ECDSA_SECP256R1_SHA256, +#if !defined(BORINGSSL_ANDROID_SYSTEM) + SSL_SIGN_RSA_PSS_SHA256, +#endif + SSL_SIGN_RSA_PKCS1_SHA256, + + /* If needed, sign larger hashes. + * + * TODO(davidben): Determine which of these may be pruned. */ + SSL_SIGN_ECDSA_SECP384R1_SHA384, +#if !defined(BORINGSSL_ANDROID_SYSTEM) + SSL_SIGN_RSA_PSS_SHA384, +#endif + SSL_SIGN_RSA_PKCS1_SHA384, + + SSL_SIGN_ECDSA_SECP521R1_SHA512, +#if !defined(BORINGSSL_ANDROID_SYSTEM) + SSL_SIGN_RSA_PSS_SHA512, +#endif + SSL_SIGN_RSA_PKCS1_SHA512, + + /* If the peer supports nothing else, sign with SHA-1. */ + SSL_SIGN_ECDSA_SHA1, + SSL_SIGN_RSA_PKCS1_SHA1, +}; + +size_t tls12_get_verify_sigalgs(const SSL *ssl, const uint16_t **out) { + *out = kVerifySignatureAlgorithms; + return OPENSSL_ARRAY_SIZE(kVerifySignatureAlgorithms); +} + +int tls12_check_peer_sigalg(SSL *ssl, int *out_alert, uint16_t sigalg) { + const uint16_t *verify_sigalgs; + size_t num_verify_sigalgs = tls12_get_verify_sigalgs(ssl, &verify_sigalgs); + for (size_t i = 0; i < num_verify_sigalgs; i++) { + if (sigalg == verify_sigalgs[i]) { + return 1; + } + } + + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SIGNATURE_TYPE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; +} + +/* tls_extension represents a TLS extension that is handled internally. The + * |init| function is called for each handshake, before any other functions of + * the extension. Then the add and parse callbacks are called as needed. + * + * The parse callbacks receive a |CBS| that contains the contents of the + * extension (i.e. not including the type and length bytes). If an extension is + * not received then the parse callbacks will be called with a NULL CBS so that + * they can do any processing needed to handle the absence of an extension. + * + * The add callbacks receive a |CBB| to which the extension can be appended but + * the function is responsible for appending the type and length bytes too. + * + * All callbacks return one for success and zero for error. If a parse function + * returns zero then a fatal alert with value |*out_alert| will be sent. If + * |*out_alert| isn't set, then a |decode_error| alert will be sent. */ +struct tls_extension { + uint16_t value; + void (*init)(SSL_HANDSHAKE *hs); + + int (*add_clienthello)(SSL_HANDSHAKE *hs, CBB *out); + int (*parse_serverhello)(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents); + + int (*parse_clienthello)(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents); + int (*add_serverhello)(SSL_HANDSHAKE *hs, CBB *out); +}; + +static int forbid_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + if (contents != NULL) { + /* Servers MUST NOT send this extension. */ + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + return 0; + } + + return 1; +} + +static int ignore_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + /* This extension from the client is handled elsewhere. */ + return 1; +} + +static int dont_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + return 1; +} + +/* Server name indication (SNI). + * + * https://tools.ietf.org/html/rfc6066#section-3. */ + +static int ext_sni_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (ssl->tlsext_hostname == NULL) { + return 1; + } + + CBB contents, server_name_list, name; + if (!CBB_add_u16(out, TLSEXT_TYPE_server_name) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &server_name_list) || + !CBB_add_u8(&server_name_list, TLSEXT_NAMETYPE_host_name) || + !CBB_add_u16_length_prefixed(&server_name_list, &name) || + !CBB_add_bytes(&name, (const uint8_t *)ssl->tlsext_hostname, + strlen(ssl->tlsext_hostname)) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_sni_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + if (CBS_len(contents) != 0) { + return 0; + } + + assert(ssl->tlsext_hostname != NULL); + + if (ssl->session == NULL) { + OPENSSL_free(hs->new_session->tlsext_hostname); + hs->new_session->tlsext_hostname = BUF_strdup(ssl->tlsext_hostname); + if (!hs->new_session->tlsext_hostname) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + } + + return 1; +} + +static int ext_sni_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + CBS server_name_list, host_name; + uint8_t name_type; + if (!CBS_get_u16_length_prefixed(contents, &server_name_list) || + !CBS_get_u8(&server_name_list, &name_type) || + /* Although the server_name extension was intended to be extensible to + * new name types and multiple names, OpenSSL 1.0.x had a bug which meant + * different name types will cause an error. Further, RFC 4366 originally + * defined syntax inextensibly. RFC 6066 corrected this mistake, but + * adding new name types is no longer feasible. + * + * Act as if the extensibility does not exist to simplify parsing. */ + !CBS_get_u16_length_prefixed(&server_name_list, &host_name) || + CBS_len(&server_name_list) != 0 || + CBS_len(contents) != 0) { + return 0; + } + + if (name_type != TLSEXT_NAMETYPE_host_name || + CBS_len(&host_name) == 0 || + CBS_len(&host_name) > TLSEXT_MAXLEN_host_name || + CBS_contains_zero_byte(&host_name)) { + *out_alert = SSL_AD_UNRECOGNIZED_NAME; + return 0; + } + + /* Copy the hostname as a string. */ + if (!CBS_strdup(&host_name, &hs->hostname)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + hs->should_ack_sni = 1; + return 1; +} + +static int ext_sni_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + if (hs->ssl->s3->session_reused || + !hs->should_ack_sni) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_server_name) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + + +/* Renegotiation indication. + * + * https://tools.ietf.org/html/rfc5746 */ + +static int ext_ri_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + /* Renegotiation indication is not necessary in TLS 1.3. */ + if (min_version >= TLS1_3_VERSION) { + return 1; + } + + assert(ssl->s3->initial_handshake_complete == + (ssl->s3->previous_client_finished_len != 0)); + + CBB contents, prev_finished; + if (!CBB_add_u16(out, TLSEXT_TYPE_renegotiate) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &prev_finished) || + !CBB_add_bytes(&prev_finished, ssl->s3->previous_client_finished, + ssl->s3->previous_client_finished_len) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_ri_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents != NULL && ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 0; + } + + /* Servers may not switch between omitting the extension and supporting it. + * See RFC 5746, sections 3.5 and 4.2. */ + if (ssl->s3->initial_handshake_complete && + (contents != NULL) != ssl->s3->send_connection_binding) { + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + return 0; + } + + if (contents == NULL) { + /* Strictly speaking, if we want to avoid an attack we should *always* see + * RI even on initial ServerHello because the client doesn't see any + * renegotiation during an attack. However this would mean we could not + * connect to any server which doesn't support RI. + * + * OpenSSL has |SSL_OP_LEGACY_SERVER_CONNECT| to control this, but in + * practical terms every client sets it so it's just assumed here. */ + return 1; + } + + const size_t expected_len = ssl->s3->previous_client_finished_len + + ssl->s3->previous_server_finished_len; + + /* Check for logic errors */ + assert(!expected_len || ssl->s3->previous_client_finished_len); + assert(!expected_len || ssl->s3->previous_server_finished_len); + assert(ssl->s3->initial_handshake_complete == + (ssl->s3->previous_client_finished_len != 0)); + assert(ssl->s3->initial_handshake_complete == + (ssl->s3->previous_server_finished_len != 0)); + + /* Parse out the extension contents. */ + CBS renegotiated_connection; + if (!CBS_get_u8_length_prefixed(contents, &renegotiated_connection) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_ENCODING_ERR); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + /* Check that the extension matches. */ + if (CBS_len(&renegotiated_connection) != expected_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + + const uint8_t *d = CBS_data(&renegotiated_connection); + if (CRYPTO_memcmp(d, ssl->s3->previous_client_finished, + ssl->s3->previous_client_finished_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + d += ssl->s3->previous_client_finished_len; + + if (CRYPTO_memcmp(d, ssl->s3->previous_server_finished, + ssl->s3->previous_server_finished_len)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + ssl->s3->send_connection_binding = 1; + + return 1; +} + +static int ext_ri_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + /* Renegotiation isn't supported as a server so this function should never be + * called after the initial handshake. */ + assert(!ssl->s3->initial_handshake_complete); + + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 1; + } + + if (contents == NULL) { + return 1; + } + + CBS renegotiated_connection; + if (!CBS_get_u8_length_prefixed(contents, &renegotiated_connection) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_ENCODING_ERR); + return 0; + } + + /* Check that the extension matches. We do not support renegotiation as a + * server, so this must be empty. */ + if (CBS_len(&renegotiated_connection) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + + ssl->s3->send_connection_binding = 1; + + return 1; +} + +static int ext_ri_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + /* Renegotiation isn't supported as a server so this function should never be + * called after the initial handshake. */ + assert(!ssl->s3->initial_handshake_complete); + + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_renegotiate) || + !CBB_add_u16(out, 1 /* length */) || + !CBB_add_u8(out, 0 /* empty renegotiation info */)) { + return 0; + } + + return 1; +} + + +/* Extended Master Secret. + * + * https://tools.ietf.org/html/rfc7627 */ + +static int ext_ems_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + uint16_t min_version, max_version; + if (!ssl_get_version_range(hs->ssl, &min_version, &max_version)) { + return 0; + } + + /* Extended master secret is not necessary in TLS 1.3. */ + if (min_version >= TLS1_3_VERSION || max_version <= SSL3_VERSION) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_extended_master_secret) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + +static int ext_ems_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + + if (contents != NULL) { + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION || + ssl->version == SSL3_VERSION || + CBS_len(contents) != 0) { + return 0; + } + + hs->extended_master_secret = 1; + } + + /* Whether EMS is negotiated may not change on renegotiation. */ + if (ssl->s3->established_session != NULL && + hs->extended_master_secret != + ssl->s3->established_session->extended_master_secret) { + OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_EMS_MISMATCH); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + return 1; +} + +static int ext_ems_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + uint16_t version = ssl3_protocol_version(hs->ssl); + if (version >= TLS1_3_VERSION || + version == SSL3_VERSION) { + return 1; + } + + if (contents == NULL) { + return 1; + } + + if (CBS_len(contents) != 0) { + return 0; + } + + hs->extended_master_secret = 1; + return 1; +} + +static int ext_ems_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + if (!hs->extended_master_secret) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_extended_master_secret) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + + +/* Session tickets. + * + * https://tools.ietf.org/html/rfc5077 */ + +static int ext_ticket_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + /* TLS 1.3 uses a different ticket extension. */ + if (min_version >= TLS1_3_VERSION || + SSL_get_options(ssl) & SSL_OP_NO_TICKET) { + return 1; + } + + const uint8_t *ticket_data = NULL; + int ticket_len = 0; + + /* Renegotiation does not participate in session resumption. However, still + * advertise the extension to avoid potentially breaking servers which carry + * over the state from the previous handshake, such as OpenSSL servers + * without upstream's 3c3f0259238594d77264a78944d409f2127642c4. */ + uint16_t session_version; + if (!ssl->s3->initial_handshake_complete && + ssl->session != NULL && + ssl->session->tlsext_tick != NULL && + /* Don't send TLS 1.3 session tickets in the ticket extension. */ + ssl->method->version_from_wire(&session_version, + ssl->session->ssl_version) && + session_version < TLS1_3_VERSION) { + ticket_data = ssl->session->tlsext_tick; + ticket_len = ssl->session->tlsext_ticklen; + } + + CBB ticket; + if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) || + !CBB_add_u16_length_prefixed(out, &ticket) || + !CBB_add_bytes(&ticket, ticket_data, ticket_len) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_ticket_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 0; + } + + /* If |SSL_OP_NO_TICKET| is set then no extension will have been sent and + * this function should never be called, even if the server tries to send the + * extension. */ + assert((SSL_get_options(ssl) & SSL_OP_NO_TICKET) == 0); + + if (CBS_len(contents) != 0) { + return 0; + } + + hs->ticket_expected = 1; + return 1; +} + +static int ext_ticket_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + if (!hs->ticket_expected) { + return 1; + } + + /* If |SSL_OP_NO_TICKET| is set, |ticket_expected| should never be true. */ + assert((SSL_get_options(hs->ssl) & SSL_OP_NO_TICKET) == 0); + + if (!CBB_add_u16(out, TLSEXT_TYPE_session_ticket) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + + +/* Signature Algorithms. + * + * https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ + +static int ext_sigalgs_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + if (max_version < TLS1_2_VERSION) { + return 1; + } + + const uint16_t *sigalgs; + const size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs); + + CBB contents, sigalgs_cbb; + if (!CBB_add_u16(out, TLSEXT_TYPE_signature_algorithms) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &sigalgs_cbb)) { + return 0; + } + + for (size_t i = 0; i < num_sigalgs; i++) { + if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) { + return 0; + } + } + + if (!CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_sigalgs_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + OPENSSL_free(hs->peer_sigalgs); + hs->peer_sigalgs = NULL; + hs->num_peer_sigalgs = 0; + + if (contents == NULL) { + return 1; + } + + CBS supported_signature_algorithms; + if (!CBS_get_u16_length_prefixed(contents, &supported_signature_algorithms) || + CBS_len(contents) != 0 || + CBS_len(&supported_signature_algorithms) == 0 || + !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) { + return 0; + } + + return 1; +} + + +/* OCSP Stapling. + * + * https://tools.ietf.org/html/rfc6066#section-8 */ + +static int ext_ocsp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (!ssl->ocsp_stapling_enabled) { + return 1; + } + + CBB contents; + if (!CBB_add_u16(out, TLSEXT_TYPE_status_request) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) || + !CBB_add_u16(&contents, 0 /* empty responder ID list */) || + !CBB_add_u16(&contents, 0 /* empty request extensions */) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_ocsp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + /* TLS 1.3 OCSP responses are included in the Certificate extensions. */ + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 0; + } + + /* OCSP stapling is forbidden on non-certificate ciphers. */ + if (CBS_len(contents) != 0 || + !ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + return 0; + } + + /* Note this does not check for resumption in TLS 1.2. Sending + * status_request here does not make sense, but OpenSSL does so and the + * specification does not say anything. Tolerate it but ignore it. */ + + hs->certificate_status_expected = 1; + return 1; +} + +static int ext_ocsp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + uint8_t status_type; + if (!CBS_get_u8(contents, &status_type)) { + return 0; + } + + /* We cannot decide whether OCSP stapling will occur yet because the correct + * SSL_CTX might not have been selected. */ + hs->ocsp_stapling_requested = status_type == TLSEXT_STATUSTYPE_ocsp; + + return 1; +} + +static int ext_ocsp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION || + !hs->ocsp_stapling_requested || + ssl->cert->ocsp_response == NULL || + ssl->s3->session_reused || + !ssl_cipher_uses_certificate_auth(hs->new_cipher)) { + return 1; + } + + hs->certificate_status_expected = 1; + + return CBB_add_u16(out, TLSEXT_TYPE_status_request) && + CBB_add_u16(out, 0 /* length */); +} + + +/* Next protocol negotiation. + * + * https://htmlpreview.github.io/?https://github.com/agl/technotes/blob/master/nextprotoneg.html */ + +static int ext_npn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (ssl->s3->initial_handshake_complete || + ssl->ctx->next_proto_select_cb == NULL || + SSL_is_dtls(ssl)) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_next_proto_neg) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + +static int ext_npn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 0; + } + + /* If any of these are false then we should never have sent the NPN + * extension in the ClientHello and thus this function should never have been + * called. */ + assert(!ssl->s3->initial_handshake_complete); + assert(!SSL_is_dtls(ssl)); + assert(ssl->ctx->next_proto_select_cb != NULL); + + if (ssl->s3->alpn_selected != NULL) { + /* NPN and ALPN may not be negotiated in the same connection. */ + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_BOTH_NPN_AND_ALPN); + return 0; + } + + const uint8_t *const orig_contents = CBS_data(contents); + const size_t orig_len = CBS_len(contents); + + while (CBS_len(contents) != 0) { + CBS proto; + if (!CBS_get_u8_length_prefixed(contents, &proto) || + CBS_len(&proto) == 0) { + return 0; + } + } + + uint8_t *selected; + uint8_t selected_len; + if (ssl->ctx->next_proto_select_cb( + ssl, &selected, &selected_len, orig_contents, orig_len, + ssl->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + OPENSSL_free(ssl->s3->next_proto_negotiated); + ssl->s3->next_proto_negotiated = BUF_memdup(selected, selected_len); + if (ssl->s3->next_proto_negotiated == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + ssl->s3->next_proto_negotiated_len = selected_len; + hs->next_proto_neg_seen = 1; + + return 1; +} + +static int ext_npn_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 1; + } + + if (contents != NULL && CBS_len(contents) != 0) { + return 0; + } + + if (contents == NULL || + ssl->s3->initial_handshake_complete || + ssl->ctx->next_protos_advertised_cb == NULL || + SSL_is_dtls(ssl)) { + return 1; + } + + hs->next_proto_neg_seen = 1; + return 1; +} + +static int ext_npn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + /* |next_proto_neg_seen| might have been cleared when an ALPN extension was + * parsed. */ + if (!hs->next_proto_neg_seen) { + return 1; + } + + const uint8_t *npa; + unsigned npa_len; + + if (ssl->ctx->next_protos_advertised_cb( + ssl, &npa, &npa_len, ssl->ctx->next_protos_advertised_cb_arg) != + SSL_TLSEXT_ERR_OK) { + hs->next_proto_neg_seen = 0; + return 1; + } + + CBB contents; + if (!CBB_add_u16(out, TLSEXT_TYPE_next_proto_neg) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_bytes(&contents, npa, npa_len) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + + +/* Signed certificate timestamps. + * + * https://tools.ietf.org/html/rfc6962#section-3.3.1 */ + +static int ext_sct_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (!ssl->signed_cert_timestamps_enabled) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + +static int ext_sct_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + /* TLS 1.3 SCTs are included in the Certificate extensions. */ + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* If this is false then we should never have sent the SCT extension in the + * ClientHello and thus this function should never have been called. */ + assert(ssl->signed_cert_timestamps_enabled); + + if (!ssl_is_sct_list_valid(contents)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Session resumption uses the original session information. The extension + * should not be sent on resumption, but RFC 6962 did not make it a + * requirement, so tolerate this. + * + * TODO(davidben): Enforce this anyway. */ + if (!ssl->s3->session_reused && + !CBS_stow(contents, &hs->new_session->tlsext_signed_cert_timestamp_list, + &hs->new_session->tlsext_signed_cert_timestamp_list_length)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + return 1; +} + +static int ext_sct_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + if (CBS_len(contents) != 0) { + return 0; + } + + hs->scts_requested = 1; + return 1; +} + +static int ext_sct_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + /* The extension shouldn't be sent when resuming sessions. */ + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION || + ssl->s3->session_reused || + ssl->cert->signed_cert_timestamp_list == NULL) { + return 1; + } + + CBB contents; + return CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) && + CBB_add_u16_length_prefixed(out, &contents) && + CBB_add_bytes( + &contents, + CRYPTO_BUFFER_data(ssl->cert->signed_cert_timestamp_list), + CRYPTO_BUFFER_len(ssl->cert->signed_cert_timestamp_list)) && + CBB_flush(out); +} + + +/* Application-level Protocol Negotiation. + * + * https://tools.ietf.org/html/rfc7301 */ + +static int ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (ssl->alpn_client_proto_list == NULL || + ssl->s3->initial_handshake_complete) { + return 1; + } + + CBB contents, proto_list; + if (!CBB_add_u16(out, TLSEXT_TYPE_application_layer_protocol_negotiation) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &proto_list) || + !CBB_add_bytes(&proto_list, ssl->alpn_client_proto_list, + ssl->alpn_client_proto_list_len) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_alpn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + assert(!ssl->s3->initial_handshake_complete); + assert(ssl->alpn_client_proto_list != NULL); + + if (hs->next_proto_neg_seen) { + /* NPN and ALPN may not be negotiated in the same connection. */ + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_BOTH_NPN_AND_ALPN); + return 0; + } + + /* The extension data consists of a ProtocolNameList which must have + * exactly one ProtocolName. Each of these is length-prefixed. */ + CBS protocol_name_list, protocol_name; + if (!CBS_get_u16_length_prefixed(contents, &protocol_name_list) || + CBS_len(contents) != 0 || + !CBS_get_u8_length_prefixed(&protocol_name_list, &protocol_name) || + /* Empty protocol names are forbidden. */ + CBS_len(&protocol_name) == 0 || + CBS_len(&protocol_name_list) != 0) { + return 0; + } + + /* Check that the protcol name is one of the ones we advertised. */ + int protocol_ok = 0; + CBS client_protocol_name_list, client_protocol_name; + CBS_init(&client_protocol_name_list, ssl->alpn_client_proto_list, + ssl->alpn_client_proto_list_len); + while (CBS_len(&client_protocol_name_list) > 0) { + if (!CBS_get_u8_length_prefixed(&client_protocol_name_list, + &client_protocol_name)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + if (CBS_len(&client_protocol_name) == CBS_len(&protocol_name) && + OPENSSL_memcmp(CBS_data(&client_protocol_name), + CBS_data(&protocol_name), + CBS_len(&protocol_name)) == 0) { + protocol_ok = 1; + break; + } + } + + if (!protocol_ok) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + if (!CBS_stow(&protocol_name, &ssl->s3->alpn_selected, + &ssl->s3->alpn_selected_len)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + return 1; +} + +int ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert, + const SSL_CLIENT_HELLO *client_hello) { + SSL *const ssl = hs->ssl; + CBS contents; + if (ssl->ctx->alpn_select_cb == NULL || + !ssl_client_hello_get_extension( + client_hello, &contents, + TLSEXT_TYPE_application_layer_protocol_negotiation)) { + /* Ignore ALPN if not configured or no extension was supplied. */ + return 1; + } + + /* ALPN takes precedence over NPN. */ + hs->next_proto_neg_seen = 0; + + CBS protocol_name_list; + if (!CBS_get_u16_length_prefixed(&contents, &protocol_name_list) || + CBS_len(&contents) != 0 || + CBS_len(&protocol_name_list) < 2) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Validate the protocol list. */ + CBS protocol_name_list_copy = protocol_name_list; + while (CBS_len(&protocol_name_list_copy) > 0) { + CBS protocol_name; + + if (!CBS_get_u8_length_prefixed(&protocol_name_list_copy, &protocol_name) || + /* Empty protocol names are forbidden. */ + CBS_len(&protocol_name) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + } + + const uint8_t *selected; + uint8_t selected_len; + if (ssl->ctx->alpn_select_cb( + ssl, &selected, &selected_len, CBS_data(&protocol_name_list), + CBS_len(&protocol_name_list), + ssl->ctx->alpn_select_cb_arg) == SSL_TLSEXT_ERR_OK) { + OPENSSL_free(ssl->s3->alpn_selected); + ssl->s3->alpn_selected = BUF_memdup(selected, selected_len); + if (ssl->s3->alpn_selected == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + ssl->s3->alpn_selected_len = selected_len; + } + + return 1; +} + +static int ext_alpn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (ssl->s3->alpn_selected == NULL) { + return 1; + } + + CBB contents, proto_list, proto; + if (!CBB_add_u16(out, TLSEXT_TYPE_application_layer_protocol_negotiation) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &proto_list) || + !CBB_add_u8_length_prefixed(&proto_list, &proto) || + !CBB_add_bytes(&proto, ssl->s3->alpn_selected, + ssl->s3->alpn_selected_len) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + + +/* Channel ID. + * + * https://tools.ietf.org/html/draft-balfanz-tls-channelid-01 */ + +static void ext_channel_id_init(SSL_HANDSHAKE *hs) { + hs->ssl->s3->tlsext_channel_id_valid = 0; +} + +static int ext_channel_id_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (!ssl->tlsext_channel_id_enabled || + SSL_is_dtls(ssl)) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_channel_id) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + +static int ext_channel_id_parse_serverhello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + assert(!SSL_is_dtls(ssl)); + assert(ssl->tlsext_channel_id_enabled); + + if (CBS_len(contents) != 0) { + return 0; + } + + ssl->s3->tlsext_channel_id_valid = 1; + return 1; +} + +static int ext_channel_id_parse_clienthello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL || + !ssl->tlsext_channel_id_enabled || + SSL_is_dtls(ssl)) { + return 1; + } + + if (CBS_len(contents) != 0) { + return 0; + } + + ssl->s3->tlsext_channel_id_valid = 1; + return 1; +} + +static int ext_channel_id_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (!ssl->s3->tlsext_channel_id_valid) { + return 1; + } + + if (!CBB_add_u16(out, TLSEXT_TYPE_channel_id) || + !CBB_add_u16(out, 0 /* length */)) { + return 0; + } + + return 1; +} + + +/* Secure Real-time Transport Protocol (SRTP) extension. + * + * https://tools.ietf.org/html/rfc5764 */ + + +static void ext_srtp_init(SSL_HANDSHAKE *hs) { + hs->ssl->srtp_profile = NULL; +} + +static int ext_srtp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl); + if (profiles == NULL) { + return 1; + } + const size_t num_profiles = sk_SRTP_PROTECTION_PROFILE_num(profiles); + if (num_profiles == 0) { + return 1; + } + + CBB contents, profile_ids; + if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &profile_ids)) { + return 0; + } + + for (size_t i = 0; i < num_profiles; i++) { + if (!CBB_add_u16(&profile_ids, + sk_SRTP_PROTECTION_PROFILE_value(profiles, i)->id)) { + return 0; + } + } + + if (!CBB_add_u8(&contents, 0 /* empty use_mki value */) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_srtp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + /* The extension consists of a u16-prefixed profile ID list containing a + * single uint16_t profile ID, then followed by a u8-prefixed srtp_mki field. + * + * See https://tools.ietf.org/html/rfc5764#section-4.1.1 */ + CBS profile_ids, srtp_mki; + uint16_t profile_id; + if (!CBS_get_u16_length_prefixed(contents, &profile_ids) || + !CBS_get_u16(&profile_ids, &profile_id) || + CBS_len(&profile_ids) != 0 || + !CBS_get_u8_length_prefixed(contents, &srtp_mki) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + return 0; + } + + if (CBS_len(&srtp_mki) != 0) { + /* Must be no MKI, since we never offer one. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_MKI_VALUE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl); + + /* Check to see if the server gave us something we support (and presumably + * offered). */ + for (size_t i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(profiles); i++) { + const SRTP_PROTECTION_PROFILE *profile = + sk_SRTP_PROTECTION_PROFILE_value(profiles, i); + + if (profile->id == profile_id) { + ssl->srtp_profile = profile; + return 1; + } + } + + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; +} + +static int ext_srtp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + CBS profile_ids, srtp_mki; + if (!CBS_get_u16_length_prefixed(contents, &profile_ids) || + CBS_len(&profile_ids) < 2 || + !CBS_get_u8_length_prefixed(contents, &srtp_mki) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + return 0; + } + /* Discard the MKI value for now. */ + + const STACK_OF(SRTP_PROTECTION_PROFILE) *server_profiles = + SSL_get_srtp_profiles(ssl); + + /* Pick the server's most preferred profile. */ + for (size_t i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(server_profiles); i++) { + const SRTP_PROTECTION_PROFILE *server_profile = + sk_SRTP_PROTECTION_PROFILE_value(server_profiles, i); + + CBS profile_ids_tmp; + CBS_init(&profile_ids_tmp, CBS_data(&profile_ids), CBS_len(&profile_ids)); + + while (CBS_len(&profile_ids_tmp) > 0) { + uint16_t profile_id; + if (!CBS_get_u16(&profile_ids_tmp, &profile_id)) { + return 0; + } + + if (server_profile->id == profile_id) { + ssl->srtp_profile = server_profile; + return 1; + } + } + } + + return 1; +} + +static int ext_srtp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (ssl->srtp_profile == NULL) { + return 1; + } + + CBB contents, profile_ids; + if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &profile_ids) || + !CBB_add_u16(&profile_ids, ssl->srtp_profile->id) || + !CBB_add_u8(&contents, 0 /* empty MKI */) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + + +/* EC point formats. + * + * https://tools.ietf.org/html/rfc4492#section-5.1.2 */ + +static int ext_ec_point_add_extension(SSL_HANDSHAKE *hs, CBB *out) { + CBB contents, formats; + if (!CBB_add_u16(out, TLSEXT_TYPE_ec_point_formats) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &formats) || + !CBB_add_u8(&formats, TLSEXT_ECPOINTFORMAT_uncompressed) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + +static int ext_ec_point_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + uint16_t min_version, max_version; + if (!ssl_get_version_range(hs->ssl, &min_version, &max_version)) { + return 0; + } + + /* The point format extension is unneccessary in TLS 1.3. */ + if (min_version >= TLS1_3_VERSION) { + return 1; + } + + return ext_ec_point_add_extension(hs, out); +} + +static int ext_ec_point_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + if (ssl3_protocol_version(hs->ssl) >= TLS1_3_VERSION) { + return 0; + } + + CBS ec_point_format_list; + if (!CBS_get_u8_length_prefixed(contents, &ec_point_format_list) || + CBS_len(contents) != 0) { + return 0; + } + + /* Per RFC 4492, section 5.1.2, implementations MUST support the uncompressed + * point format. */ + if (OPENSSL_memchr(CBS_data(&ec_point_format_list), + TLSEXT_ECPOINTFORMAT_uncompressed, + CBS_len(&ec_point_format_list)) == NULL) { + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + return 1; +} + +static int ext_ec_point_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, + CBS *contents) { + if (ssl3_protocol_version(hs->ssl) >= TLS1_3_VERSION) { + return 1; + } + + return ext_ec_point_parse_serverhello(hs, out_alert, contents); +} + +static int ext_ec_point_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + return 1; + } + + const uint32_t alg_k = hs->new_cipher->algorithm_mkey; + const uint32_t alg_a = hs->new_cipher->algorithm_auth; + const int using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA); + + if (!using_ecc) { + return 1; + } + + return ext_ec_point_add_extension(hs, out); +} + + +/* Pre Shared Key + * + * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.6 */ + +static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + uint16_t session_version; + if (max_version < TLS1_3_VERSION || ssl->session == NULL || + !ssl->method->version_from_wire(&session_version, + ssl->session->ssl_version) || + session_version < TLS1_3_VERSION) { + return 0; + } + + const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl); + if (digest == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + size_t binder_len = EVP_MD_size(digest); + return 15 + ssl->session->tlsext_ticklen + binder_len; +} + +static int ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + uint16_t session_version; + if (max_version < TLS1_3_VERSION || ssl->session == NULL || + !ssl->method->version_from_wire(&session_version, + ssl->session->ssl_version) || + session_version < TLS1_3_VERSION) { + return 1; + } + + struct OPENSSL_timeval now; + ssl_get_current_time(ssl, &now); + uint32_t ticket_age = 1000 * (now.tv_sec - ssl->session->time); + uint32_t obfuscated_ticket_age = ticket_age + ssl->session->ticket_age_add; + + /* Fill in a placeholder zero binder of the appropriate length. It will be + * computed and filled in later after length prefixes are computed. */ + uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0}; + + const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl); + if (digest == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + size_t binder_len = EVP_MD_size(digest); + + CBB contents, identity, ticket, binders, binder; + if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &identity) || + !CBB_add_u16_length_prefixed(&identity, &ticket) || + !CBB_add_bytes(&ticket, ssl->session->tlsext_tick, + ssl->session->tlsext_ticklen) || + !CBB_add_u32(&identity, obfuscated_ticket_age) || + !CBB_add_u16_length_prefixed(&contents, &binders) || + !CBB_add_u8_length_prefixed(&binders, &binder) || + !CBB_add_bytes(&binder, zero_binder, binder_len)) { + return 0; + } + + hs->needs_psk_binder = 1; + return CBB_flush(out); +} + +int ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, + CBS *contents) { + uint16_t psk_id; + if (!CBS_get_u16(contents, &psk_id) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* We only advertise one PSK identity, so the only legal index is zero. */ + if (psk_id != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_NOT_FOUND); + *out_alert = SSL_AD_UNKNOWN_PSK_IDENTITY; + return 0; + } + + return 1; +} + +int ssl_ext_pre_shared_key_parse_clienthello( + SSL_HANDSHAKE *hs, CBS *out_ticket, CBS *out_binders, + uint32_t *out_obfuscated_ticket_age, uint8_t *out_alert, CBS *contents) { + /* We only process the first PSK identity since we don't support pure PSK. */ + CBS identities, binders; + if (!CBS_get_u16_length_prefixed(contents, &identities) || + !CBS_get_u16_length_prefixed(&identities, out_ticket) || + !CBS_get_u32(&identities, out_obfuscated_ticket_age) || + !CBS_get_u16_length_prefixed(contents, &binders) || + CBS_len(&binders) == 0 || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + *out_binders = binders; + + /* Check the syntax of the remaining identities, but do not process them. */ + size_t num_identities = 1; + while (CBS_len(&identities) != 0) { + CBS unused_ticket; + uint32_t unused_obfuscated_ticket_age; + if (!CBS_get_u16_length_prefixed(&identities, &unused_ticket) || + !CBS_get_u32(&identities, &unused_obfuscated_ticket_age)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + num_identities++; + } + + /* Check the syntax of the binders. The value will be checked later if + * resuming. */ + size_t num_binders = 0; + while (CBS_len(&binders) != 0) { + CBS binder; + if (!CBS_get_u8_length_prefixed(&binders, &binder)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + num_binders++; + } + + if (num_identities != num_binders) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_IDENTITY_BINDER_COUNT_MISMATCH); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + return 1; +} + +int ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + if (!hs->ssl->s3->session_reused) { + return 1; + } + + CBB contents; + if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) || + !CBB_add_u16_length_prefixed(out, &contents) || + /* We only consider the first identity for resumption */ + !CBB_add_u16(&contents, 0) || + !CBB_flush(out)) { + return 0; + } + + return 1; +} + + +/* Pre-Shared Key Exchange Modes + * + * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.7 */ + +static int ext_psk_key_exchange_modes_add_clienthello(SSL_HANDSHAKE *hs, + CBB *out) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + if (max_version < TLS1_3_VERSION) { + return 1; + } + + CBB contents, ke_modes; + if (!CBB_add_u16(out, TLSEXT_TYPE_psk_key_exchange_modes) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &ke_modes) || + !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE)) { + return 0; + } + + return CBB_flush(out); +} + +static int ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + CBS ke_modes; + if (!CBS_get_u8_length_prefixed(contents, &ke_modes) || + CBS_len(&ke_modes) == 0 || + CBS_len(contents) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* We only support tickets with PSK_DHE_KE. */ + hs->accept_psk_mode = OPENSSL_memchr(CBS_data(&ke_modes), SSL_PSK_DHE_KE, + CBS_len(&ke_modes)) != NULL; + + return 1; +} + + +/* Early Data Indication + * + * https://tools.ietf.org/html/draft-ietf-tls-tls13-18#section-4.2.8 */ + +static int ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + /* TODO(svaldez): Support 0RTT. */ + return 1; +} + +static int ext_early_data_parse_clienthello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, CBS *contents) { + SSL *const ssl = hs->ssl; + if (contents == NULL) { + return 1; + } + + if (CBS_len(contents) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Since we don't currently accept 0-RTT, we have to skip past any early data + * the client might have sent. */ + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + ssl->s3->skip_early_data = 1; + } + return 1; +} + + +/* Key Share + * + * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.5 */ + +static int ext_key_share_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + if (max_version < TLS1_3_VERSION) { + return 1; + } + + CBB contents, kse_bytes; + if (!CBB_add_u16(out, TLSEXT_TYPE_key_share) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &kse_bytes)) { + return 0; + } + + uint16_t group_id = hs->retry_group; + if (hs->received_hello_retry_request) { + /* We received a HelloRetryRequest without a new curve, so there is no new + * share to append. Leave |ecdh_ctx| as-is. */ + if (group_id == 0 && + !CBB_add_bytes(&kse_bytes, hs->key_share_bytes, + hs->key_share_bytes_len)) { + return 0; + } + OPENSSL_free(hs->key_share_bytes); + hs->key_share_bytes = NULL; + hs->key_share_bytes_len = 0; + if (group_id == 0) { + return CBB_flush(out); + } + } else { + /* Add a fake group. See draft-davidben-tls-grease-01. */ + if (ssl->ctx->grease_enabled && + (!CBB_add_u16(&kse_bytes, + ssl_get_grease_value(ssl, ssl_grease_group)) || + !CBB_add_u16(&kse_bytes, 1 /* length */) || + !CBB_add_u8(&kse_bytes, 0 /* one byte key share */))) { + return 0; + } + + /* Predict the most preferred group. */ + const uint16_t *groups; + size_t groups_len; + tls1_get_grouplist(ssl, &groups, &groups_len); + if (groups_len == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_GROUPS_SPECIFIED); + return 0; + } + + group_id = groups[0]; + } + + CBB key_exchange; + if (!CBB_add_u16(&kse_bytes, group_id) || + !CBB_add_u16_length_prefixed(&kse_bytes, &key_exchange) || + !SSL_ECDH_CTX_init(&hs->ecdh_ctx, group_id) || + !SSL_ECDH_CTX_offer(&hs->ecdh_ctx, &key_exchange) || + !CBB_flush(&kse_bytes)) { + return 0; + } + + if (!hs->received_hello_retry_request) { + /* Save the contents of the extension to repeat it in the second + * ClientHello. */ + hs->key_share_bytes_len = CBB_len(&kse_bytes); + hs->key_share_bytes = BUF_memdup(CBB_data(&kse_bytes), CBB_len(&kse_bytes)); + if (hs->key_share_bytes == NULL) { + return 0; + } + } + + return CBB_flush(out); +} + +int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t **out_secret, + size_t *out_secret_len, + uint8_t *out_alert, CBS *contents) { + CBS peer_key; + uint16_t group_id; + if (!CBS_get_u16(contents, &group_id) || + !CBS_get_u16_length_prefixed(contents, &peer_key) || + CBS_len(contents) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (SSL_ECDH_CTX_get_id(&hs->ecdh_ctx) != group_id) { + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE); + return 0; + } + + if (!SSL_ECDH_CTX_finish(&hs->ecdh_ctx, out_secret, out_secret_len, out_alert, + CBS_data(&peer_key), CBS_len(&peer_key))) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + hs->new_session->group_id = group_id; + SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx); + return 1; +} + +int ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, int *out_found, + uint8_t **out_secret, + size_t *out_secret_len, + uint8_t *out_alert, CBS *contents) { + uint16_t group_id; + CBS key_shares; + if (!tls1_get_shared_group(hs, &group_id)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_GROUP); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + + if (!CBS_get_u16_length_prefixed(contents, &key_shares) || + CBS_len(contents) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return 0; + } + + /* Find the corresponding key share. */ + int found = 0; + CBS peer_key; + while (CBS_len(&key_shares) > 0) { + uint16_t id; + CBS peer_key_tmp; + if (!CBS_get_u16(&key_shares, &id) || + !CBS_get_u16_length_prefixed(&key_shares, &peer_key_tmp)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return 0; + } + + if (id == group_id) { + if (found) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_KEY_SHARE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + found = 1; + peer_key = peer_key_tmp; + /* Continue parsing the structure to keep peers honest. */ + } + } + + if (!found) { + *out_found = 0; + *out_secret = NULL; + *out_secret_len = 0; + return 1; + } + + /* Compute the DH secret. */ + uint8_t *secret = NULL; + size_t secret_len; + SSL_ECDH_CTX group; + OPENSSL_memset(&group, 0, sizeof(SSL_ECDH_CTX)); + CBB public_key; + if (!CBB_init(&public_key, 32) || + !SSL_ECDH_CTX_init(&group, group_id) || + !SSL_ECDH_CTX_accept(&group, &public_key, &secret, &secret_len, out_alert, + CBS_data(&peer_key), CBS_len(&peer_key)) || + !CBB_finish(&public_key, &hs->public_key, &hs->public_key_len)) { + OPENSSL_free(secret); + SSL_ECDH_CTX_cleanup(&group); + CBB_cleanup(&public_key); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + SSL_ECDH_CTX_cleanup(&group); + + *out_secret = secret; + *out_secret_len = secret_len; + *out_found = 1; + return 1; +} + +int ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + uint16_t group_id; + CBB kse_bytes, public_key; + if (!tls1_get_shared_group(hs, &group_id) || + !CBB_add_u16(out, TLSEXT_TYPE_key_share) || + !CBB_add_u16_length_prefixed(out, &kse_bytes) || + !CBB_add_u16(&kse_bytes, group_id) || + !CBB_add_u16_length_prefixed(&kse_bytes, &public_key) || + !CBB_add_bytes(&public_key, hs->public_key, hs->public_key_len) || + !CBB_flush(out)) { + return 0; + } + + OPENSSL_free(hs->public_key); + hs->public_key = NULL; + hs->public_key_len = 0; + + hs->new_session->group_id = group_id; + return 1; +} + + +/* Supported Versions + * + * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.1 */ + +static int ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + uint16_t min_version, max_version; + if (!ssl_get_version_range(ssl, &min_version, &max_version)) { + return 0; + } + + if (max_version <= TLS1_2_VERSION) { + return 1; + } + + CBB contents, versions; + if (!CBB_add_u16(out, TLSEXT_TYPE_supported_versions) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &versions)) { + return 0; + } + + /* Add a fake version. See draft-davidben-tls-grease-01. */ + if (ssl->ctx->grease_enabled && + !CBB_add_u16(&versions, ssl_get_grease_value(ssl, ssl_grease_version))) { + return 0; + } + + for (uint16_t version = max_version; version >= min_version; version--) { + if (!CBB_add_u16(&versions, ssl->method->version_to_wire(version))) { + return 0; + } + } + + if (!CBB_flush(out)) { + return 0; + } + + return 1; +} + + +/* Cookie + * + * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.2 */ + +static int ext_cookie_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + if (hs->cookie == NULL) { + return 1; + } + + CBB contents, cookie; + if (!CBB_add_u16(out, TLSEXT_TYPE_cookie) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &cookie) || + !CBB_add_bytes(&cookie, hs->cookie, hs->cookie_len) || + !CBB_flush(out)) { + return 0; + } + + /* The cookie is no longer needed in memory. */ + OPENSSL_free(hs->cookie); + hs->cookie = NULL; + hs->cookie_len = 0; + return 1; +} + + +/* Negotiated Groups + * + * https://tools.ietf.org/html/rfc4492#section-5.1.2 + * https://tools.ietf.org/html/draft-ietf-tls-tls13-16#section-4.2.4 */ + +static int ext_supported_groups_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + CBB contents, groups_bytes; + if (!CBB_add_u16(out, TLSEXT_TYPE_supported_groups) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u16_length_prefixed(&contents, &groups_bytes)) { + return 0; + } + + /* Add a fake group. See draft-davidben-tls-grease-01. */ + if (ssl->ctx->grease_enabled && + !CBB_add_u16(&groups_bytes, + ssl_get_grease_value(ssl, ssl_grease_group))) { + return 0; + } + + const uint16_t *groups; + size_t groups_len; + tls1_get_grouplist(ssl, &groups, &groups_len); + + for (size_t i = 0; i < groups_len; i++) { + if (!CBB_add_u16(&groups_bytes, groups[i])) { + return 0; + } + } + + return CBB_flush(out); +} + +static int ext_supported_groups_parse_serverhello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, + CBS *contents) { + /* This extension is not expected to be echoed by servers in TLS 1.2, but some + * BigIP servers send it nonetheless, so do not enforce this. */ + return 1; +} + +static int ext_supported_groups_parse_clienthello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, + CBS *contents) { + if (contents == NULL) { + return 1; + } + + CBS supported_group_list; + if (!CBS_get_u16_length_prefixed(contents, &supported_group_list) || + CBS_len(&supported_group_list) == 0 || + (CBS_len(&supported_group_list) & 1) != 0 || + CBS_len(contents) != 0) { + return 0; + } + + hs->peer_supported_group_list = + OPENSSL_malloc(CBS_len(&supported_group_list)); + if (hs->peer_supported_group_list == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + const size_t num_groups = CBS_len(&supported_group_list) / 2; + for (size_t i = 0; i < num_groups; i++) { + if (!CBS_get_u16(&supported_group_list, + &hs->peer_supported_group_list[i])) { + goto err; + } + } + + assert(CBS_len(&supported_group_list) == 0); + hs->peer_supported_group_list_len = num_groups; + + return 1; + +err: + OPENSSL_free(hs->peer_supported_group_list); + hs->peer_supported_group_list = NULL; + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; +} + +static int ext_supported_groups_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { + /* Servers don't echo this extension. */ + return 1; +} + + +/* kExtensions contains all the supported extensions. */ +static const struct tls_extension kExtensions[] = { + { + TLSEXT_TYPE_renegotiate, + NULL, + ext_ri_add_clienthello, + ext_ri_parse_serverhello, + ext_ri_parse_clienthello, + ext_ri_add_serverhello, + }, + { + TLSEXT_TYPE_server_name, + NULL, + ext_sni_add_clienthello, + ext_sni_parse_serverhello, + ext_sni_parse_clienthello, + ext_sni_add_serverhello, + }, + { + TLSEXT_TYPE_extended_master_secret, + NULL, + ext_ems_add_clienthello, + ext_ems_parse_serverhello, + ext_ems_parse_clienthello, + ext_ems_add_serverhello, + }, + { + TLSEXT_TYPE_session_ticket, + NULL, + ext_ticket_add_clienthello, + ext_ticket_parse_serverhello, + /* Ticket extension client parsing is handled in ssl_session.c */ + ignore_parse_clienthello, + ext_ticket_add_serverhello, + }, + { + TLSEXT_TYPE_signature_algorithms, + NULL, + ext_sigalgs_add_clienthello, + forbid_parse_serverhello, + ext_sigalgs_parse_clienthello, + dont_add_serverhello, + }, + { + TLSEXT_TYPE_status_request, + NULL, + ext_ocsp_add_clienthello, + ext_ocsp_parse_serverhello, + ext_ocsp_parse_clienthello, + ext_ocsp_add_serverhello, + }, + { + TLSEXT_TYPE_next_proto_neg, + NULL, + ext_npn_add_clienthello, + ext_npn_parse_serverhello, + ext_npn_parse_clienthello, + ext_npn_add_serverhello, + }, + { + TLSEXT_TYPE_certificate_timestamp, + NULL, + ext_sct_add_clienthello, + ext_sct_parse_serverhello, + ext_sct_parse_clienthello, + ext_sct_add_serverhello, + }, + { + TLSEXT_TYPE_application_layer_protocol_negotiation, + NULL, + ext_alpn_add_clienthello, + ext_alpn_parse_serverhello, + /* ALPN is negotiated late in |ssl_negotiate_alpn|. */ + ignore_parse_clienthello, + ext_alpn_add_serverhello, + }, + { + TLSEXT_TYPE_channel_id, + ext_channel_id_init, + ext_channel_id_add_clienthello, + ext_channel_id_parse_serverhello, + ext_channel_id_parse_clienthello, + ext_channel_id_add_serverhello, + }, + { + TLSEXT_TYPE_srtp, + ext_srtp_init, + ext_srtp_add_clienthello, + ext_srtp_parse_serverhello, + ext_srtp_parse_clienthello, + ext_srtp_add_serverhello, + }, + { + TLSEXT_TYPE_ec_point_formats, + NULL, + ext_ec_point_add_clienthello, + ext_ec_point_parse_serverhello, + ext_ec_point_parse_clienthello, + ext_ec_point_add_serverhello, + }, + { + TLSEXT_TYPE_key_share, + NULL, + ext_key_share_add_clienthello, + forbid_parse_serverhello, + ignore_parse_clienthello, + dont_add_serverhello, + }, + { + TLSEXT_TYPE_psk_key_exchange_modes, + NULL, + ext_psk_key_exchange_modes_add_clienthello, + forbid_parse_serverhello, + ext_psk_key_exchange_modes_parse_clienthello, + dont_add_serverhello, + }, + { + TLSEXT_TYPE_early_data, + NULL, + ext_early_data_add_clienthello, + forbid_parse_serverhello, + ext_early_data_parse_clienthello, + dont_add_serverhello, + }, + { + TLSEXT_TYPE_supported_versions, + NULL, + ext_supported_versions_add_clienthello, + forbid_parse_serverhello, + ignore_parse_clienthello, + dont_add_serverhello, + }, + { + TLSEXT_TYPE_cookie, + NULL, + ext_cookie_add_clienthello, + forbid_parse_serverhello, + ignore_parse_clienthello, + dont_add_serverhello, + }, + /* The final extension must be non-empty. WebSphere Application Server 7.0 is + * intolerant to the last extension being zero-length. See + * https://crbug.com/363583. */ + { + TLSEXT_TYPE_supported_groups, + NULL, + ext_supported_groups_add_clienthello, + ext_supported_groups_parse_serverhello, + ext_supported_groups_parse_clienthello, + ext_supported_groups_add_serverhello, + }, +}; + +#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension)) + +OPENSSL_COMPILE_ASSERT(kNumExtensions <= + sizeof(((SSL_HANDSHAKE *)NULL)->extensions.sent) * 8, + too_many_extensions_for_sent_bitset); +OPENSSL_COMPILE_ASSERT( + kNumExtensions <= sizeof(((SSL_HANDSHAKE *)NULL)->extensions.received) * 8, + too_many_extensions_for_received_bitset); + +static const struct tls_extension *tls_extension_find(uint32_t *out_index, + uint16_t value) { + unsigned i; + for (i = 0; i < kNumExtensions; i++) { + if (kExtensions[i].value == value) { + *out_index = i; + return &kExtensions[i]; + } + } + + return NULL; +} + +int SSL_extension_supported(unsigned extension_value) { + uint32_t index; + return extension_value == TLSEXT_TYPE_padding || + tls_extension_find(&index, extension_value) != NULL; +} + +int ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, size_t header_len) { + SSL *const ssl = hs->ssl; + /* Don't add extensions for SSLv3 unless doing secure renegotiation. */ + if (hs->client_version == SSL3_VERSION && + !ssl->s3->send_connection_binding) { + return 1; + } + + CBB extensions; + if (!CBB_add_u16_length_prefixed(out, &extensions)) { + goto err; + } + + hs->extensions.sent = 0; + hs->custom_extensions.sent = 0; + + for (size_t i = 0; i < kNumExtensions; i++) { + if (kExtensions[i].init != NULL) { + kExtensions[i].init(hs); + } + } + + uint16_t grease_ext1 = 0; + if (ssl->ctx->grease_enabled) { + /* Add a fake empty extension. See draft-davidben-tls-grease-01. */ + grease_ext1 = ssl_get_grease_value(ssl, ssl_grease_extension1); + if (!CBB_add_u16(&extensions, grease_ext1) || + !CBB_add_u16(&extensions, 0 /* zero length */)) { + goto err; + } + } + + for (size_t i = 0; i < kNumExtensions; i++) { + const size_t len_before = CBB_len(&extensions); + if (!kExtensions[i].add_clienthello(hs, &extensions)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION); + ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value); + goto err; + } + + if (CBB_len(&extensions) != len_before) { + hs->extensions.sent |= (1u << i); + } + } + + if (!custom_ext_add_clienthello(hs, &extensions)) { + goto err; + } + + if (ssl->ctx->grease_enabled) { + /* Add a fake non-empty extension. See draft-davidben-tls-grease-01. */ + uint16_t grease_ext2 = ssl_get_grease_value(ssl, ssl_grease_extension2); + + /* The two fake extensions must not have the same value. GREASE values are + * of the form 0x1a1a, 0x2a2a, 0x3a3a, etc., so XOR to generate a different + * one. */ + if (grease_ext1 == grease_ext2) { + grease_ext2 ^= 0x1010; + } + + if (!CBB_add_u16(&extensions, grease_ext2) || + !CBB_add_u16(&extensions, 1 /* one byte length */) || + !CBB_add_u8(&extensions, 0 /* single zero byte as contents */)) { + goto err; + } + } + + if (!SSL_is_dtls(ssl)) { + size_t psk_extension_len = ext_pre_shared_key_clienthello_length(hs); + header_len += 2 + CBB_len(&extensions) + psk_extension_len; + if (header_len > 0xff && header_len < 0x200) { + /* Add padding to workaround bugs in F5 terminators. See RFC 7685. + * + * NB: because this code works out the length of all existing extensions + * it MUST always appear last. */ + size_t padding_len = 0x200 - header_len; + /* Extensions take at least four bytes to encode. Always include at least + * one byte of data if including the extension. WebSphere Application + * Server 7.0 is intolerant to the last extension being zero-length. See + * https://crbug.com/363583. */ + if (padding_len >= 4 + 1) { + padding_len -= 4; + } else { + padding_len = 1; + } + + uint8_t *padding_bytes; + if (!CBB_add_u16(&extensions, TLSEXT_TYPE_padding) || + !CBB_add_u16(&extensions, padding_len) || + !CBB_add_space(&extensions, &padding_bytes, padding_len)) { + goto err; + } + + OPENSSL_memset(padding_bytes, 0, padding_len); + } + } + + /* The PSK extension must be last, including after the padding. */ + if (!ext_pre_shared_key_add_clienthello(hs, &extensions)) { + goto err; + } + + /* Discard empty extensions blocks. */ + if (CBB_len(&extensions) == 0) { + CBB_discard_child(out); + } + + return CBB_flush(out); + +err: + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; +} + +int ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out) { + SSL *const ssl = hs->ssl; + CBB extensions; + if (!CBB_add_u16_length_prefixed(out, &extensions)) { + goto err; + } + + for (unsigned i = 0; i < kNumExtensions; i++) { + if (!(hs->extensions.received & (1u << i))) { + /* Don't send extensions that were not received. */ + continue; + } + + if (!kExtensions[i].add_serverhello(hs, &extensions)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION); + ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value); + goto err; + } + } + + if (!custom_ext_add_serverhello(hs, &extensions)) { + goto err; + } + + /* Discard empty extensions blocks before TLS 1.3. */ + if (ssl3_protocol_version(ssl) < TLS1_3_VERSION && + CBB_len(&extensions) == 0) { + CBB_discard_child(out); + } + + return CBB_flush(out); + +err: + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; +} + +static int ssl_scan_clienthello_tlsext(SSL_HANDSHAKE *hs, + const SSL_CLIENT_HELLO *client_hello, + int *out_alert) { + SSL *const ssl = hs->ssl; + for (size_t i = 0; i < kNumExtensions; i++) { + if (kExtensions[i].init != NULL) { + kExtensions[i].init(hs); + } + } + + hs->extensions.received = 0; + hs->custom_extensions.received = 0; + + CBS extensions; + CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len); + while (CBS_len(&extensions) != 0) { + uint16_t type; + CBS extension; + + /* Decode the next extension. */ + if (!CBS_get_u16(&extensions, &type) || + !CBS_get_u16_length_prefixed(&extensions, &extension)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* RFC 5746 made the existence of extensions in SSL 3.0 somewhat + * ambiguous. Ignore all but the renegotiation_info extension. */ + if (ssl->version == SSL3_VERSION && type != TLSEXT_TYPE_renegotiate) { + continue; + } + + unsigned ext_index; + const struct tls_extension *const ext = + tls_extension_find(&ext_index, type); + + if (ext == NULL) { + if (!custom_ext_parse_clienthello(hs, out_alert, type, &extension)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION); + return 0; + } + continue; + } + + hs->extensions.received |= (1u << ext_index); + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ext->parse_clienthello(hs, &alert, &extension)) { + *out_alert = alert; + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION); + ERR_add_error_dataf("extension %u", (unsigned)type); + return 0; + } + } + + for (size_t i = 0; i < kNumExtensions; i++) { + if (hs->extensions.received & (1u << i)) { + continue; + } + + CBS *contents = NULL, fake_contents; + static const uint8_t kFakeRenegotiateExtension[] = {0}; + if (kExtensions[i].value == TLSEXT_TYPE_renegotiate && + ssl_client_cipher_list_contains_cipher(client_hello, + SSL3_CK_SCSV & 0xffff)) { + /* The renegotiation SCSV was received so pretend that we received a + * renegotiation extension. */ + CBS_init(&fake_contents, kFakeRenegotiateExtension, + sizeof(kFakeRenegotiateExtension)); + contents = &fake_contents; + hs->extensions.received |= (1u << i); + } + + /* Extension wasn't observed so call the callback with a NULL + * parameter. */ + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!kExtensions[i].parse_clienthello(hs, &alert, contents)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION); + ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value); + *out_alert = alert; + return 0; + } + } + + return 1; +} + +int ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs, + const SSL_CLIENT_HELLO *client_hello) { + SSL *const ssl = hs->ssl; + int alert = SSL_AD_DECODE_ERROR; + if (ssl_scan_clienthello_tlsext(hs, client_hello, &alert) <= 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return 0; + } + + if (ssl_check_clienthello_tlsext(hs) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_TLSEXT); + return 0; + } + + return 1; +} + +static int ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs, + int *out_alert) { + SSL *const ssl = hs->ssl; + /* Before TLS 1.3, ServerHello extensions blocks may be omitted if empty. */ + if (CBS_len(cbs) == 0 && ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + return 1; + } + + /* Decode the extensions block and check it is valid. */ + CBS extensions; + if (!CBS_get_u16_length_prefixed(cbs, &extensions) || + !tls1_check_duplicate_extensions(&extensions)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + uint32_t received = 0; + while (CBS_len(&extensions) != 0) { + uint16_t type; + CBS extension; + + /* Decode the next extension. */ + if (!CBS_get_u16(&extensions, &type) || + !CBS_get_u16_length_prefixed(&extensions, &extension)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + unsigned ext_index; + const struct tls_extension *const ext = + tls_extension_find(&ext_index, type); + + if (ext == NULL) { + if (!custom_ext_parse_serverhello(hs, out_alert, type, &extension)) { + return 0; + } + continue; + } + + OPENSSL_COMPILE_ASSERT(kNumExtensions <= sizeof(hs->extensions.sent) * 8, + too_many_bits); + + if (!(hs->extensions.sent & (1u << ext_index)) && + type != TLSEXT_TYPE_renegotiate) { + /* If the extension was never sent then it is illegal, except for the + * renegotiation extension which, in SSL 3.0, is signaled via SCSV. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + ERR_add_error_dataf("extension :%u", (unsigned)type); + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + received |= (1u << ext_index); + + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ext->parse_serverhello(hs, &alert, &extension)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION); + ERR_add_error_dataf("extension %u", (unsigned)type); + *out_alert = alert; + return 0; + } + } + + for (size_t i = 0; i < kNumExtensions; i++) { + if (!(received & (1u << i))) { + /* Extension wasn't observed so call the callback with a NULL + * parameter. */ + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!kExtensions[i].parse_serverhello(hs, &alert, NULL)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION); + ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value); + *out_alert = alert; + return 0; + } + } + } + + return 1; +} + +static int ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = SSL_TLSEXT_ERR_NOACK; + int al = SSL_AD_UNRECOGNIZED_NAME; + + if (ssl->ctx->tlsext_servername_callback != 0) { + ret = ssl->ctx->tlsext_servername_callback(ssl, &al, + ssl->ctx->tlsext_servername_arg); + } else if (ssl->session_ctx->tlsext_servername_callback != 0) { + ret = ssl->session_ctx->tlsext_servername_callback( + ssl, &al, ssl->session_ctx->tlsext_servername_arg); + } + + switch (ret) { + case SSL_TLSEXT_ERR_ALERT_FATAL: + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); + return -1; + + case SSL_TLSEXT_ERR_NOACK: + hs->should_ack_sni = 0; + return 1; + + default: + return 1; + } +} + +int ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) { + SSL *const ssl = hs->ssl; + int alert = SSL_AD_DECODE_ERROR; + if (ssl_scan_serverhello_tlsext(hs, cbs, &alert) <= 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return 0; + } + + return 1; +} + +static enum ssl_ticket_aead_result_t +ssl_decrypt_ticket_with_cipher_ctx(SSL *ssl, uint8_t **out, size_t *out_len, + int *out_renew_ticket, const uint8_t *ticket, + size_t ticket_len) { + enum ssl_ticket_aead_result_t ret = ssl_ticket_aead_ignore_ticket; + const SSL_CTX *const ssl_ctx = ssl->session_ctx; + uint8_t *plaintext = NULL; + + HMAC_CTX hmac_ctx; + HMAC_CTX_init(&hmac_ctx); + EVP_CIPHER_CTX cipher_ctx; + EVP_CIPHER_CTX_init(&cipher_ctx); + + /* Ensure there is room for the key name and the largest IV + * |tlsext_ticket_key_cb| may try to consume. The real limit may be lower, but + * the maximum IV length should be well under the minimum size for the + * session material and HMAC. */ + if (ticket_len < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) { + goto out; + } + const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN; + + if (ssl_ctx->tlsext_ticket_key_cb != NULL) { + int cb_ret = ssl_ctx->tlsext_ticket_key_cb( + ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, &cipher_ctx, + &hmac_ctx, 0 /* decrypt */); + if (cb_ret < 0) { + ret = ssl_ticket_aead_error; + goto out; + } else if (cb_ret == 0) { + goto out; + } else if (cb_ret == 2) { + *out_renew_ticket = 1; + } + } else { + /* Check the key name matches. */ + if (OPENSSL_memcmp(ticket, ssl_ctx->tlsext_tick_key_name, + SSL_TICKET_KEY_NAME_LEN) != 0) { + goto out; + } + if (!HMAC_Init_ex(&hmac_ctx, ssl_ctx->tlsext_tick_hmac_key, + sizeof(ssl_ctx->tlsext_tick_hmac_key), tlsext_tick_md(), + NULL) || + !EVP_DecryptInit_ex(&cipher_ctx, EVP_aes_128_cbc(), NULL, + ssl_ctx->tlsext_tick_aes_key, iv)) { + ret = ssl_ticket_aead_error; + goto out; + } + } + size_t iv_len = EVP_CIPHER_CTX_iv_length(&cipher_ctx); + + /* Check the MAC at the end of the ticket. */ + uint8_t mac[EVP_MAX_MD_SIZE]; + size_t mac_len = HMAC_size(&hmac_ctx); + if (ticket_len < SSL_TICKET_KEY_NAME_LEN + iv_len + 1 + mac_len) { + /* The ticket must be large enough for key name, IV, data, and MAC. */ + goto out; + } + HMAC_Update(&hmac_ctx, ticket, ticket_len - mac_len); + HMAC_Final(&hmac_ctx, mac, NULL); + int mac_ok = + CRYPTO_memcmp(mac, ticket + (ticket_len - mac_len), mac_len) == 0; +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + mac_ok = 1; +#endif + if (!mac_ok) { + goto out; + } + + /* Decrypt the session data. */ + const uint8_t *ciphertext = ticket + SSL_TICKET_KEY_NAME_LEN + iv_len; + size_t ciphertext_len = ticket_len - SSL_TICKET_KEY_NAME_LEN - iv_len - + mac_len; + plaintext = OPENSSL_malloc(ciphertext_len); + if (plaintext == NULL) { + ret = ssl_ticket_aead_error; + goto out; + } + size_t plaintext_len; +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + OPENSSL_memcpy(plaintext, ciphertext, ciphertext_len); + plaintext_len = ciphertext_len; +#else + if (ciphertext_len >= INT_MAX) { + goto out; + } + int len1, len2; + if (!EVP_DecryptUpdate(&cipher_ctx, plaintext, &len1, ciphertext, + (int)ciphertext_len) || + !EVP_DecryptFinal_ex(&cipher_ctx, plaintext + len1, &len2)) { + ERR_clear_error(); + goto out; + } + plaintext_len = (size_t)(len1) + len2; +#endif + + *out = plaintext; + plaintext = NULL; + *out_len = plaintext_len; + ret = ssl_ticket_aead_success; + +out: + OPENSSL_free(plaintext); + HMAC_CTX_cleanup(&hmac_ctx); + EVP_CIPHER_CTX_cleanup(&cipher_ctx); + return ret; +} + +static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_method( + SSL *ssl, uint8_t **out, size_t *out_len, int *out_renew_ticket, + const uint8_t *ticket, size_t ticket_len) { + uint8_t *plaintext = OPENSSL_malloc(ticket_len); + if (plaintext == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return ssl_ticket_aead_error; + } + + size_t plaintext_len; + const enum ssl_ticket_aead_result_t result = + ssl->session_ctx->ticket_aead_method->open( + ssl, plaintext, &plaintext_len, ticket_len, ticket, ticket_len); + + if (result == ssl_ticket_aead_success) { + *out = plaintext; + plaintext = NULL; + *out_len = plaintext_len; + } + + OPENSSL_free(plaintext); + return result; +} + +enum ssl_ticket_aead_result_t ssl_process_ticket( + SSL *ssl, SSL_SESSION **out_session, int *out_renew_ticket, + const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id, + size_t session_id_len) { + *out_renew_ticket = 0; + *out_session = NULL; + + if ((SSL_get_options(ssl) & SSL_OP_NO_TICKET) || + session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { + return ssl_ticket_aead_ignore_ticket; + } + + uint8_t *plaintext = NULL; + size_t plaintext_len; + enum ssl_ticket_aead_result_t result; + if (ssl->session_ctx->ticket_aead_method != NULL) { + result = ssl_decrypt_ticket_with_method( + ssl, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len); + } else { + result = ssl_decrypt_ticket_with_cipher_ctx( + ssl, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len); + } + + if (result != ssl_ticket_aead_success) { + return result; + } + + /* Decode the session. */ + SSL_SESSION *session = + SSL_SESSION_from_bytes(plaintext, plaintext_len, ssl->ctx); + OPENSSL_free(plaintext); + + if (session == NULL) { + ERR_clear_error(); /* Don't leave an error on the queue. */ + return ssl_ticket_aead_ignore_ticket; + } + + /* Copy the client's session ID into the new session, to denote the ticket has + * been accepted. */ + OPENSSL_memcpy(session->session_id, session_id, session_id_len); + session->session_id_length = session_id_len; + + *out_session = session; + return ssl_ticket_aead_success; +} + +int tls1_parse_peer_sigalgs(SSL_HANDSHAKE *hs, const CBS *in_sigalgs) { + /* Extension ignored for inappropriate versions */ + if (ssl3_protocol_version(hs->ssl) < TLS1_2_VERSION) { + return 1; + } + + OPENSSL_free(hs->peer_sigalgs); + hs->peer_sigalgs = NULL; + hs->num_peer_sigalgs = 0; + + size_t num_sigalgs = CBS_len(in_sigalgs); + if (num_sigalgs % 2 != 0) { + return 0; + } + num_sigalgs /= 2; + + /* supported_signature_algorithms in the certificate request is + * allowed to be empty. */ + if (num_sigalgs == 0) { + return 1; + } + + /* This multiplication doesn't overflow because sizeof(uint16_t) is two + * and we just divided |num_sigalgs| by two. */ + hs->peer_sigalgs = OPENSSL_malloc(num_sigalgs * sizeof(uint16_t)); + if (hs->peer_sigalgs == NULL) { + return 0; + } + hs->num_peer_sigalgs = num_sigalgs; + + CBS sigalgs; + CBS_init(&sigalgs, CBS_data(in_sigalgs), CBS_len(in_sigalgs)); + for (size_t i = 0; i < num_sigalgs; i++) { + if (!CBS_get_u16(&sigalgs, &hs->peer_sigalgs[i])) { + return 0; + } + } + + return 1; +} + +int tls1_choose_signature_algorithm(SSL_HANDSHAKE *hs, uint16_t *out) { + SSL *const ssl = hs->ssl; + CERT *cert = ssl->cert; + + /* Before TLS 1.2, the signature algorithm isn't negotiated as part of the + * handshake. It is fixed at MD5-SHA1 for RSA and SHA1 for ECDSA. */ + if (ssl3_protocol_version(ssl) < TLS1_2_VERSION) { + int type = ssl_private_key_type(ssl); + if (type == NID_rsaEncryption) { + *out = SSL_SIGN_RSA_PKCS1_MD5_SHA1; + return 1; + } + if (ssl_is_ecdsa_key_type(type)) { + *out = SSL_SIGN_ECDSA_SHA1; + return 1; + } + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS); + return 0; + } + + const uint16_t *sigalgs = cert->sigalgs; + size_t num_sigalgs = cert->num_sigalgs; + if (sigalgs == NULL) { + sigalgs = kSignSignatureAlgorithms; + num_sigalgs = OPENSSL_ARRAY_SIZE(kSignSignatureAlgorithms); + } + + const uint16_t *peer_sigalgs = hs->peer_sigalgs; + size_t num_peer_sigalgs = hs->num_peer_sigalgs; + if (num_peer_sigalgs == 0 && ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + /* If the client didn't specify any signature_algorithms extension then + * we can assume that it supports SHA1. See + * http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */ + static const uint16_t kDefaultPeerAlgorithms[] = {SSL_SIGN_RSA_PKCS1_SHA1, + SSL_SIGN_ECDSA_SHA1}; + peer_sigalgs = kDefaultPeerAlgorithms; + num_peer_sigalgs = OPENSSL_ARRAY_SIZE(kDefaultPeerAlgorithms); + } + + for (size_t i = 0; i < num_sigalgs; i++) { + uint16_t sigalg = sigalgs[i]; + /* SSL_SIGN_RSA_PKCS1_MD5_SHA1 is an internal value and should never be + * negotiated. */ + if (sigalg == SSL_SIGN_RSA_PKCS1_MD5_SHA1 || + !ssl_private_key_supports_signature_algorithm(ssl, sigalgs[i])) { + continue; + } + + for (size_t j = 0; j < num_peer_sigalgs; j++) { + if (sigalg == peer_sigalgs[j]) { + *out = sigalg; + return 1; + } + } + } + + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS); + return 0; +} + +int tls1_verify_channel_id(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = 0; + uint16_t extension_type; + CBS extension, channel_id; + + /* A Channel ID handshake message is structured to contain multiple + * extensions, but the only one that can be present is Channel ID. */ + CBS_init(&channel_id, ssl->init_msg, ssl->init_num); + if (!CBS_get_u16(&channel_id, &extension_type) || + !CBS_get_u16_length_prefixed(&channel_id, &extension) || + CBS_len(&channel_id) != 0 || + extension_type != TLSEXT_TYPE_channel_id || + CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return 0; + } + + EC_GROUP *p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + if (!p256) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT); + return 0; + } + + EC_KEY *key = NULL; + EC_POINT *point = NULL; + BIGNUM x, y; + ECDSA_SIG sig; + BN_init(&x); + BN_init(&y); + sig.r = BN_new(); + sig.s = BN_new(); + if (sig.r == NULL || sig.s == NULL) { + goto err; + } + + const uint8_t *p = CBS_data(&extension); + if (BN_bin2bn(p + 0, 32, &x) == NULL || + BN_bin2bn(p + 32, 32, &y) == NULL || + BN_bin2bn(p + 64, 32, sig.r) == NULL || + BN_bin2bn(p + 96, 32, sig.s) == NULL) { + goto err; + } + + point = EC_POINT_new(p256); + if (point == NULL || + !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) { + goto err; + } + + key = EC_KEY_new(); + if (key == NULL || + !EC_KEY_set_group(key, p256) || + !EC_KEY_set_public_key(key, point)) { + goto err; + } + + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_len; + if (!tls1_channel_id_hash(hs, digest, &digest_len)) { + goto err; + } + + int sig_ok = ECDSA_do_verify(digest, digest_len, &sig, key); +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + sig_ok = 1; +#endif + if (!sig_ok) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR); + ssl->s3->tlsext_channel_id_valid = 0; + goto err; + } + + OPENSSL_memcpy(ssl->s3->tlsext_channel_id, p, 64); + ret = 1; + +err: + BN_free(&x); + BN_free(&y); + BN_free(sig.r); + BN_free(sig.s); + EC_KEY_free(key); + EC_POINT_free(point); + EC_GROUP_free(p256); + return ret; +} + +int tls1_write_channel_id(SSL_HANDSHAKE *hs, CBB *cbb) { + SSL *const ssl = hs->ssl; + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_len; + if (!tls1_channel_id_hash(hs, digest, &digest_len)) { + return 0; + } + + EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private); + if (ec_key == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + int ret = 0; + BIGNUM *x = BN_new(); + BIGNUM *y = BN_new(); + ECDSA_SIG *sig = NULL; + if (x == NULL || y == NULL || + !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), + EC_KEY_get0_public_key(ec_key), + x, y, NULL)) { + goto err; + } + + sig = ECDSA_do_sign(digest, digest_len, ec_key); + if (sig == NULL) { + goto err; + } + + CBB child; + if (!CBB_add_u16(cbb, TLSEXT_TYPE_channel_id) || + !CBB_add_u16_length_prefixed(cbb, &child) || + !BN_bn2cbb_padded(&child, 32, x) || + !BN_bn2cbb_padded(&child, 32, y) || + !BN_bn2cbb_padded(&child, 32, sig->r) || + !BN_bn2cbb_padded(&child, 32, sig->s) || + !CBB_flush(cbb)) { + goto err; + } + + ret = 1; + +err: + BN_free(x); + BN_free(y); + ECDSA_SIG_free(sig); + return ret; +} + +int tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len) { + SSL *const ssl = hs->ssl; + if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + uint8_t *msg; + size_t msg_len; + if (!tls13_get_cert_verify_signature_input(hs, &msg, &msg_len, + ssl_cert_verify_channel_id)) { + return 0; + } + SHA256(msg, msg_len, out); + *out_len = SHA256_DIGEST_LENGTH; + OPENSSL_free(msg); + return 1; + } + + SHA256_CTX ctx; + + SHA256_Init(&ctx); + static const char kClientIDMagic[] = "TLS Channel ID signature"; + SHA256_Update(&ctx, kClientIDMagic, sizeof(kClientIDMagic)); + + if (ssl->session != NULL) { + static const char kResumptionMagic[] = "Resumption"; + SHA256_Update(&ctx, kResumptionMagic, sizeof(kResumptionMagic)); + if (ssl->session->original_handshake_hash_len == 0) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + SHA256_Update(&ctx, ssl->session->original_handshake_hash, + ssl->session->original_handshake_hash_len); + } + + uint8_t hs_hash[EVP_MAX_MD_SIZE]; + size_t hs_hash_len; + if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, hs_hash, &hs_hash_len)) { + return 0; + } + SHA256_Update(&ctx, hs_hash, (size_t)hs_hash_len); + SHA256_Final(out, &ctx); + *out_len = SHA256_DIGEST_LENGTH; + return 1; +} + +/* tls1_record_handshake_hashes_for_channel_id records the current handshake + * hashes in |hs->new_session| so that Channel ID resumptions can sign that + * data. */ +int tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + /* This function should never be called for a resumed session because the + * handshake hashes that we wish to record are for the original, full + * handshake. */ + if (ssl->session != NULL) { + return -1; + } + + OPENSSL_COMPILE_ASSERT( + sizeof(hs->new_session->original_handshake_hash) == EVP_MAX_MD_SIZE, + original_handshake_hash_is_too_small); + + size_t digest_len; + if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, + hs->new_session->original_handshake_hash, + &digest_len)) { + return -1; + } + + OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE <= 0xff, max_md_size_is_too_large); + hs->new_session->original_handshake_hash_len = (uint8_t)digest_len; + + return 1; +} + +int ssl_do_channel_id_callback(SSL *ssl) { + if (ssl->tlsext_channel_id_private != NULL || + ssl->ctx->channel_id_cb == NULL) { + return 1; + } + + EVP_PKEY *key = NULL; + ssl->ctx->channel_id_cb(ssl, &key); + if (key == NULL) { + /* The caller should try again later. */ + return 1; + } + + int ret = SSL_set1_tls_channel_id(ssl, key); + EVP_PKEY_free(key); + return ret; +} + +int ssl_is_sct_list_valid(const CBS *contents) { + /* Shallow parse the SCT list for sanity. By the RFC + * (https://tools.ietf.org/html/rfc6962#section-3.3) neither the list nor any + * of the SCTs may be empty. */ + CBS copy = *contents; + CBS sct_list; + if (!CBS_get_u16_length_prefixed(©, &sct_list) || + CBS_len(©) != 0 || + CBS_len(&sct_list) == 0) { + return 0; + } + + while (CBS_len(&sct_list) > 0) { + CBS sct; + if (!CBS_get_u16_length_prefixed(&sct_list, &sct) || + CBS_len(&sct) == 0) { + return 0; + } + } + + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/CMakeLists.txt b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/CMakeLists.txt new file mode 100644 index 000000000..3b0790308 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/CMakeLists.txt @@ -0,0 +1,14 @@ +include_directories(../../include) + +add_executable( + bssl_shim + + async_bio.cc + bssl_shim.cc + packeted_bio.cc + test_config.cc + + $<TARGET_OBJECTS:test_support> +) + +target_link_libraries(bssl_shim ssl crypto) diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/PORTING.md b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/PORTING.md new file mode 100644 index 000000000..86ad24d0d --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/PORTING.md @@ -0,0 +1,109 @@ +# Porting to Other Implementations + +## Introduction + +This document provides an overview of the test runner and how to +integrate it with other stacks. So far we have it working with +BoringSSL and some incomplete integrations with NSS and OpenSSL. + +Note that supporting non-BoringSSL implementations is a work in +progress and interfaces may change in the future. Consumers should pin +to a particular revision rather than using BoringSSL’s master branch +directly. As we gain experience with other implementations, we hope to +make further improvements to portability, so please contact +davidben@google.com and ekr@rtfm.com if implementing a new shim. + + +## Integration Architecture + +The test runner integrates with the TLS stack under test through a +“shim”: a command line program which encapsulates the stack. By +default, the shim points to the BoringSSL shim in the same source +tree, but any program can be supplied via the `-shim-path` flag. The +runner opens up a server socket and provides the shim with a `-port` +argument that points to that socket. The shim always connects to the +runner as a TCP client even when acting as a TLS server. For DTLS, +there is a small framing layer that gives packet boundaries over +TCP. The shim can also pass a variety of command line arguments which +are used to configure the stack under test. These can be found at +`test_config.cc`. + + +The shim reports success by exiting with a `0` error code and failure by +reporting a non-zero error code and generally sending a textual error +value to stderr. Many of the tests expect specific error string (such +as `NO_SHARED_CIPHER`) that indicates what went wrong. + + +## Compatibility Issues + +There are a number of situations in which the runner might succeed +with some tests and not others: + +* Defects in the stack under test +* Features which haven’t yet been implemented +* Failure to implement one or more of the command line flags the runner uses with the shim +* Disagreement about the right behavior/interpretation of the spec + + +We have implemented several features which allow implementations to ease these compatibility issues. + +### Configuration File + +The runner can be supplied with a JSON configuration file which is +intended to allow for a per-stack mapping. This file currently takes +two directives: + + +* `DisabledTests`: A JSON map consisting of the pattern matching the + tests to be disabled as the key and some sort of reason why it was + disabled as the value. The key is used as a match against the test + name. The value is ignored and is just used for documentation + purposes so you can remember why you disabled a + test. `-include-disabled` overrides this filter. + +* `ErrorMap`: A JSON map from the internal errors the runner expects to + the error strings that your implementation spits out. Generally + you’ll need to map every error, but if you also provide the + ` -loose-errors` flag, then every un-mapped error just gets mapped to + the empty string and treated as if it matched every error the runner + expects. + + +The `-shim-config` flag is used to provide the config file. + + +### Unimplemented Features +If the shim encounters some request from the runner that it knows it +can’t fulfill (e.g., a command line flag that it doesn’t recognize), +then it can exit with the special code `89`. Shims are recommended to +use this exit code on unknown command-line arguments. + +The test runner interprets this as “unimplemented” and skips the +test. If run normally, this will cause the test runner to report that +the entire test suite failed. The `-allow-unimplemented` flag suppresses +this behavior and causes the test runner to ignore these tests for the +purpose of evaluating the success or failure of the test suite. + + +### Malloc Tests + +The test runner can also be used to stress malloc failure +codepaths. If passed `-malloc-test=0`, the runner will run each test +repeatedly with an incrementing `MALLOC_NUMBER_TO_FAIL` environment +variable. The shim should then replace the malloc implementation with +one which fails at the specified number of calls. If there are not +enough calls to reach the number, the shim should fail with exit code +`88`. This signals to the runner that the test has completed. + +See `crypto/test/malloc.cc` for an example malloc implementation. + +Note these tests are slow and will hit Go's test timeout. Pass `-timeout 72h` to +avoid crashing after 10 minutes. + + +## Example: Running Against NSS + +``` +DYLD_LIBRARY_PATH=~/dev/nss-dev/nss-sandbox/dist/Darwin15.6.0_64_DBG.OBJ/lib go test -shim-path ~/dev/nss-dev/nss-sandbox/dist/Darwin15.6.0_64_DBG.OBJ/bin/nss_bogo_shim -loose-errors -allow-unimplemented -shim-config ~/dev/nss-dev/nss-sandbox/nss/external_tests/nss_bogo_shim/config.json +``` diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/README.md b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/README.md new file mode 100644 index 000000000..7da29eb67 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/README.md @@ -0,0 +1,38 @@ +# BoringSSL SSL Tests + +This directory contains BoringSSL's protocol-level test suite. + +Testing a TLS implementation can be difficult. We need to produce invalid but +sufficiently correct handshakes to get our implementation close to its edge +cases. TLS's cryptographic steps mean we cannot use a transcript and effectively +need a TLS implementation on the other end. But we do not wish to litter +BoringSSL with options for bugs to test against. + +Instead, we use a fork of the Go `crypto/tls` package, heavily patched with +configurable bugs. This code, along with a test suite and harness written in Go, +lives in the `runner` directory. The harness runs BoringSSL via a C/C++ shim +binary which lives in this directory. All communication with the shim binary +occurs with command-line flags, sockets, and standard I/O. + +This strategy also ensures we always test against a second implementation. All +features should be implemented twice, once in C for BoringSSL and once in Go for +testing. If possible, the Go code should be suitable for potentially +upstreaming. However, sometimes test code has different needs. For example, our +test DTLS code enforces strict ordering on sequence numbers and has controlled +packet drop simulation. + +To run the tests manually, run `go test` from the `runner` directory. It takes +command-line flags found at the top of `runner/runner.go`. The `-help` option +also works after using `go test -c` to make a `runner.test` binary first. + +If adding a new test, these files may be a good starting point: + + * `runner/runner.go`: the test harness and all the individual tests. + * `runner/common.go`: contains the `Config` and `ProtocolBugs` struct which + control the Go TLS implementation's behavior. + * `test_config.h`, `test_config.cc`: the command-line flags which control the + shim's behavior. + * `bssl_shim.cc`: the shim binary itself. + +For porting the test suite to a different implementation see +[PORTING.md](./PORTING.md). diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/async_bio.cc b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/async_bio.cc new file mode 100644 index 000000000..fd351760b --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/async_bio.cc @@ -0,0 +1,191 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "async_bio.h" + +#include <errno.h> +#include <string.h> + +#include <openssl/bio.h> +#include <openssl/mem.h> + +#include "../../crypto/internal.h" + + +namespace { + +extern const BIO_METHOD g_async_bio_method; + +struct AsyncBio { + bool datagram; + bool enforce_write_quota; + size_t read_quota; + size_t write_quota; +}; + +AsyncBio *GetData(BIO *bio) { + if (bio->method != &g_async_bio_method) { + return NULL; + } + return (AsyncBio *)bio->ptr; +} + +static int AsyncWrite(BIO *bio, const char *in, int inl) { + AsyncBio *a = GetData(bio); + if (a == NULL || bio->next_bio == NULL) { + return 0; + } + + if (!a->enforce_write_quota) { + return BIO_write(bio->next_bio, in, inl); + } + + BIO_clear_retry_flags(bio); + + if (a->write_quota == 0) { + BIO_set_retry_write(bio); + errno = EAGAIN; + return -1; + } + + if (!a->datagram && (size_t)inl > a->write_quota) { + inl = a->write_quota; + } + int ret = BIO_write(bio->next_bio, in, inl); + if (ret <= 0) { + BIO_copy_next_retry(bio); + } else { + a->write_quota -= (a->datagram ? 1 : ret); + } + return ret; +} + +static int AsyncRead(BIO *bio, char *out, int outl) { + AsyncBio *a = GetData(bio); + if (a == NULL || bio->next_bio == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + if (a->read_quota == 0) { + BIO_set_retry_read(bio); + errno = EAGAIN; + return -1; + } + + if (!a->datagram && (size_t)outl > a->read_quota) { + outl = a->read_quota; + } + int ret = BIO_read(bio->next_bio, out, outl); + if (ret <= 0) { + BIO_copy_next_retry(bio); + } else { + a->read_quota -= (a->datagram ? 1 : ret); + } + return ret; +} + +static long AsyncCtrl(BIO *bio, int cmd, long num, void *ptr) { + if (bio->next_bio == NULL) { + return 0; + } + BIO_clear_retry_flags(bio); + int ret = BIO_ctrl(bio->next_bio, cmd, num, ptr); + BIO_copy_next_retry(bio); + return ret; +} + +static int AsyncNew(BIO *bio) { + AsyncBio *a = (AsyncBio *)OPENSSL_malloc(sizeof(*a)); + if (a == NULL) { + return 0; + } + OPENSSL_memset(a, 0, sizeof(*a)); + a->enforce_write_quota = true; + bio->init = 1; + bio->ptr = (char *)a; + return 1; +} + +static int AsyncFree(BIO *bio) { + if (bio == NULL) { + return 0; + } + + OPENSSL_free(bio->ptr); + bio->ptr = NULL; + bio->init = 0; + bio->flags = 0; + return 1; +} + +static long AsyncCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) { + if (bio->next_bio == NULL) { + return 0; + } + return BIO_callback_ctrl(bio->next_bio, cmd, fp); +} + +const BIO_METHOD g_async_bio_method = { + BIO_TYPE_FILTER, + "async bio", + AsyncWrite, + AsyncRead, + NULL /* puts */, + NULL /* gets */, + AsyncCtrl, + AsyncNew, + AsyncFree, + AsyncCallbackCtrl, +}; + +} // namespace + +bssl::UniquePtr<BIO> AsyncBioCreate() { + return bssl::UniquePtr<BIO>(BIO_new(&g_async_bio_method)); +} + +bssl::UniquePtr<BIO> AsyncBioCreateDatagram() { + bssl::UniquePtr<BIO> ret(BIO_new(&g_async_bio_method)); + if (!ret) { + return nullptr; + } + GetData(ret.get())->datagram = true; + return ret; +} + +void AsyncBioAllowRead(BIO *bio, size_t count) { + AsyncBio *a = GetData(bio); + if (a == NULL) { + return; + } + a->read_quota += count; +} + +void AsyncBioAllowWrite(BIO *bio, size_t count) { + AsyncBio *a = GetData(bio); + if (a == NULL) { + return; + } + a->write_quota += count; +} + +void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce) { + AsyncBio *a = GetData(bio); + if (a == NULL) { + return; + } + a->enforce_write_quota = enforce; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/async_bio.h b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/async_bio.h new file mode 100644 index 000000000..9974139d9 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/async_bio.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef HEADER_ASYNC_BIO +#define HEADER_ASYNC_BIO + +#include <openssl/bio.h> + + +// AsyncBioCreate creates a filter BIO for testing asynchronous state +// machines which consume a stream socket. Reads and writes will fail +// and return EAGAIN unless explicitly allowed. Each async BIO has a +// read quota and a write quota. Initially both are zero. As each is +// incremented, bytes are allowed to flow through the BIO. +bssl::UniquePtr<BIO> AsyncBioCreate(); + +// AsyncBioCreateDatagram creates a filter BIO for testing for +// asynchronous state machines which consume datagram sockets. The read +// and write quota count in packets rather than bytes. +bssl::UniquePtr<BIO> AsyncBioCreateDatagram(); + +// AsyncBioAllowRead increments |bio|'s read quota by |count|. +void AsyncBioAllowRead(BIO *bio, size_t count); + +// AsyncBioAllowWrite increments |bio|'s write quota by |count|. +void AsyncBioAllowWrite(BIO *bio, size_t count); + +// AsyncBioEnforceWriteQuota configures where |bio| enforces its write quota. +void AsyncBioEnforceWriteQuota(BIO *bio, bool enforce); + + +#endif // HEADER_ASYNC_BIO diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/bssl_shim.cc b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/bssl_shim.cc new file mode 100644 index 000000000..804cbbb67 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/bssl_shim.cc @@ -0,0 +1,2137 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#if !defined(__STDC_FORMAT_MACROS) +#define __STDC_FORMAT_MACROS +#endif + +#include <openssl/base.h> + +#if !defined(OPENSSL_WINDOWS) +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <unistd.h> +#else +#include <io.h> +OPENSSL_MSVC_PRAGMA(warning(push, 3)) +#include <winsock2.h> +#include <ws2tcpip.h> +OPENSSL_MSVC_PRAGMA(warning(pop)) + +OPENSSL_MSVC_PRAGMA(comment(lib, "Ws2_32.lib")) +#endif + +#include <assert.h> +#include <inttypes.h> +#include <string.h> + +#include <openssl/aead.h> +#include <openssl/bio.h> +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/cipher.h> +#include <openssl/crypto.h> +#include <openssl/dh.h> +#include <openssl/digest.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/nid.h> +#include <openssl/rand.h> +#include <openssl/ssl.h> +#include <openssl/x509.h> + +#include <memory> +#include <string> +#include <vector> + +#include "../../crypto/internal.h" +#include "async_bio.h" +#include "packeted_bio.h" +#include "test_config.h" + + +static CRYPTO_BUFFER_POOL *g_pool = nullptr; + +#if !defined(OPENSSL_WINDOWS) +static int closesocket(int sock) { + return close(sock); +} + +static void PrintSocketError(const char *func) { + perror(func); +} +#else +static void PrintSocketError(const char *func) { + fprintf(stderr, "%s: %d\n", func, WSAGetLastError()); +} +#endif + +static int Usage(const char *program) { + fprintf(stderr, "Usage: %s [flags...]\n", program); + return 1; +} + +struct TestState { + // async_bio is async BIO which pauses reads and writes. + BIO *async_bio = nullptr; + // packeted_bio is the packeted BIO which simulates read timeouts. + BIO *packeted_bio = nullptr; + bssl::UniquePtr<EVP_PKEY> channel_id; + bool cert_ready = false; + bssl::UniquePtr<SSL_SESSION> session; + bssl::UniquePtr<SSL_SESSION> pending_session; + bool early_callback_called = false; + bool handshake_done = false; + // private_key is the underlying private key used when testing custom keys. + bssl::UniquePtr<EVP_PKEY> private_key; + std::vector<uint8_t> private_key_result; + // private_key_retries is the number of times an asynchronous private key + // operation has been retried. + unsigned private_key_retries = 0; + bool got_new_session = false; + bssl::UniquePtr<SSL_SESSION> new_session; + bool ticket_decrypt_done = false; + bool alpn_select_done = false; + bool early_callback_ready = false; +}; + +static void TestStateExFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad, + int index, long argl, void *argp) { + delete ((TestState *)ptr); +} + +static int g_config_index = 0; +static int g_state_index = 0; + +static bool SetTestConfig(SSL *ssl, const TestConfig *config) { + return SSL_set_ex_data(ssl, g_config_index, (void *)config) == 1; +} + +static const TestConfig *GetTestConfig(const SSL *ssl) { + return (const TestConfig *)SSL_get_ex_data(ssl, g_config_index); +} + +static bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state) { + // |SSL_set_ex_data| takes ownership of |state| only on success. + if (SSL_set_ex_data(ssl, g_state_index, state.get()) == 1) { + state.release(); + return true; + } + return false; +} + +static TestState *GetTestState(const SSL *ssl) { + return (TestState *)SSL_get_ex_data(ssl, g_state_index); +} + +static bool LoadCertificate(bssl::UniquePtr<X509> *out_x509, + bssl::UniquePtr<STACK_OF(X509)> *out_chain, + const std::string &file) { + bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file())); + if (!bio || !BIO_read_filename(bio.get(), file.c_str())) { + return false; + } + + out_x509->reset(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + if (!*out_x509) { + return false; + } + + out_chain->reset(sk_X509_new_null()); + if (!*out_chain) { + return false; + } + + // Keep reading the certificate chain. + for (;;) { + bssl::UniquePtr<X509> cert( + PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + if (!cert) { + break; + } + + if (!sk_X509_push(out_chain->get(), cert.get())) { + return false; + } + cert.release(); // sk_X509_push takes ownership. + } + + uint32_t err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) != ERR_LIB_PEM || + ERR_GET_REASON(err) != PEM_R_NO_START_LINE) { + return false; +} + + ERR_clear_error(); + return true; +} + +static bssl::UniquePtr<EVP_PKEY> LoadPrivateKey(const std::string &file) { + bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_file())); + if (!bio || !BIO_read_filename(bio.get(), file.c_str())) { + return nullptr; + } + return bssl::UniquePtr<EVP_PKEY>( + PEM_read_bio_PrivateKey(bio.get(), NULL, NULL, NULL)); +} + +static bool FromHexDigit(uint8_t *out, char c) { + if ('0' <= c && c <= '9') { + *out = c - '0'; + return true; + } + if ('a' <= c && c <= 'f') { + *out = c - 'a' + 10; + return true; + } + if ('A' <= c && c <= 'F') { + *out = c - 'A' + 10; + return true; + } + return false; +} + +static bool HexDecode(std::string *out, const std::string &in) { + if ((in.size() & 1) != 0) { + return false; + } + + std::unique_ptr<uint8_t[]> buf(new uint8_t[in.size() / 2]); + for (size_t i = 0; i < in.size() / 2; i++) { + uint8_t high, low; + if (!FromHexDigit(&high, in[i*2]) || + !FromHexDigit(&low, in[i*2+1])) { + return false; + } + buf[i] = (high << 4) | low; + } + + out->assign(reinterpret_cast<const char *>(buf.get()), in.size() / 2); + return true; +} + +static std::vector<std::string> SplitParts(const std::string &in, + const char delim) { + std::vector<std::string> ret; + size_t start = 0; + + for (size_t i = 0; i < in.size(); i++) { + if (in[i] == delim) { + ret.push_back(in.substr(start, i - start)); + start = i + 1; + } + } + + ret.push_back(in.substr(start, std::string::npos)); + return ret; +} + +static std::vector<std::string> DecodeHexStrings( + const std::string &hex_strings) { + std::vector<std::string> ret; + const std::vector<std::string> parts = SplitParts(hex_strings, ','); + + for (const auto &part : parts) { + std::string binary; + if (!HexDecode(&binary, part)) { + fprintf(stderr, "Bad hex string: %s\n", part.c_str()); + return ret; + } + + ret.push_back(binary); + } + + return ret; +} + +static bssl::UniquePtr<STACK_OF(X509_NAME)> DecodeHexX509Names( + const std::string &hex_names) { + const std::vector<std::string> der_names = DecodeHexStrings(hex_names); + bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null()); + + for (const auto &der_name : der_names) { + const uint8_t *const data = + reinterpret_cast<const uint8_t *>(der_name.data()); + const uint8_t *derp = data; + bssl::UniquePtr<X509_NAME> name( + d2i_X509_NAME(nullptr, &derp, der_name.size())); + if (!name || derp != data + der_name.size()) { + fprintf(stderr, "Failed to parse X509_NAME.\n"); + return nullptr; + } + + if (!sk_X509_NAME_push(ret.get(), name.get())) { + return nullptr; + } + name.release(); + } + + return ret; +} + +static int AsyncPrivateKeyType(SSL *ssl) { + EVP_PKEY *key = GetTestState(ssl)->private_key.get(); + switch (EVP_PKEY_id(key)) { + case EVP_PKEY_RSA: + return NID_rsaEncryption; + case EVP_PKEY_EC: + return EC_GROUP_get_curve_name( + EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(key))); + default: + return NID_undef; + } +} + +static size_t AsyncPrivateKeyMaxSignatureLen(SSL *ssl) { + return EVP_PKEY_size(GetTestState(ssl)->private_key.get()); +} + +static ssl_private_key_result_t AsyncPrivateKeySign( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint16_t signature_algorithm, const uint8_t *in, size_t in_len) { + TestState *test_state = GetTestState(ssl); + if (!test_state->private_key_result.empty()) { + fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n"); + abort(); + } + + // Determine the hash. + const EVP_MD *md; + switch (signature_algorithm) { + case SSL_SIGN_RSA_PKCS1_SHA1: + case SSL_SIGN_ECDSA_SHA1: + md = EVP_sha1(); + break; + case SSL_SIGN_RSA_PKCS1_SHA256: + case SSL_SIGN_ECDSA_SECP256R1_SHA256: + case SSL_SIGN_RSA_PSS_SHA256: + md = EVP_sha256(); + break; + case SSL_SIGN_RSA_PKCS1_SHA384: + case SSL_SIGN_ECDSA_SECP384R1_SHA384: + case SSL_SIGN_RSA_PSS_SHA384: + md = EVP_sha384(); + break; + case SSL_SIGN_RSA_PKCS1_SHA512: + case SSL_SIGN_ECDSA_SECP521R1_SHA512: + case SSL_SIGN_RSA_PSS_SHA512: + md = EVP_sha512(); + break; + case SSL_SIGN_RSA_PKCS1_MD5_SHA1: + md = EVP_md5_sha1(); + break; + default: + fprintf(stderr, "Unknown signature algorithm %04x.\n", + signature_algorithm); + return ssl_private_key_failure; + } + + bssl::ScopedEVP_MD_CTX ctx; + EVP_PKEY_CTX *pctx; + if (!EVP_DigestSignInit(ctx.get(), &pctx, md, nullptr, + test_state->private_key.get())) { + return ssl_private_key_failure; + } + + // Configure additional signature parameters. + switch (signature_algorithm) { + case SSL_SIGN_RSA_PSS_SHA256: + case SSL_SIGN_RSA_PSS_SHA384: + case SSL_SIGN_RSA_PSS_SHA512: + if (!EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) || + !EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, + -1 /* salt len = hash len */)) { + return ssl_private_key_failure; + } + } + + // Write the signature into |test_state|. + size_t len = 0; + if (!EVP_DigestSignUpdate(ctx.get(), in, in_len) || + !EVP_DigestSignFinal(ctx.get(), nullptr, &len)) { + return ssl_private_key_failure; + } + test_state->private_key_result.resize(len); + if (!EVP_DigestSignFinal(ctx.get(), test_state->private_key_result.data(), + &len)) { + return ssl_private_key_failure; + } + test_state->private_key_result.resize(len); + + // The signature will be released asynchronously in |AsyncPrivateKeyComplete|. + return ssl_private_key_retry; +} + +static ssl_private_key_result_t AsyncPrivateKeyDecrypt( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + const uint8_t *in, size_t in_len) { + TestState *test_state = GetTestState(ssl); + if (!test_state->private_key_result.empty()) { + fprintf(stderr, + "AsyncPrivateKeyDecrypt called with operation pending.\n"); + abort(); + } + + RSA *rsa = EVP_PKEY_get0_RSA(test_state->private_key.get()); + if (rsa == NULL) { + fprintf(stderr, + "AsyncPrivateKeyDecrypt called with incorrect key type.\n"); + abort(); + } + test_state->private_key_result.resize(RSA_size(rsa)); + if (!RSA_decrypt(rsa, out_len, test_state->private_key_result.data(), + RSA_size(rsa), in, in_len, RSA_NO_PADDING)) { + return ssl_private_key_failure; + } + + test_state->private_key_result.resize(*out_len); + + // The decryption will be released asynchronously in |AsyncPrivateComplete|. + return ssl_private_key_retry; +} + +static ssl_private_key_result_t AsyncPrivateKeyComplete( + SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out) { + TestState *test_state = GetTestState(ssl); + if (test_state->private_key_result.empty()) { + fprintf(stderr, + "AsyncPrivateKeyComplete called without operation pending.\n"); + abort(); + } + + if (test_state->private_key_retries < 2) { + // Only return the decryption on the second attempt, to test both incomplete + // |decrypt| and |decrypt_complete|. + return ssl_private_key_retry; + } + + if (max_out < test_state->private_key_result.size()) { + fprintf(stderr, "Output buffer too small.\n"); + return ssl_private_key_failure; + } + OPENSSL_memcpy(out, test_state->private_key_result.data(), + test_state->private_key_result.size()); + *out_len = test_state->private_key_result.size(); + + test_state->private_key_result.clear(); + test_state->private_key_retries = 0; + return ssl_private_key_success; +} + +static const SSL_PRIVATE_KEY_METHOD g_async_private_key_method = { + AsyncPrivateKeyType, + AsyncPrivateKeyMaxSignatureLen, + AsyncPrivateKeySign, + nullptr /* sign_digest */, + AsyncPrivateKeyDecrypt, + AsyncPrivateKeyComplete, +}; + +template<typename T> +struct Free { + void operator()(T *buf) { + free(buf); + } +}; + +static bool GetCertificate(SSL *ssl, bssl::UniquePtr<X509> *out_x509, + bssl::UniquePtr<STACK_OF(X509)> *out_chain, + bssl::UniquePtr<EVP_PKEY> *out_pkey) { + const TestConfig *config = GetTestConfig(ssl); + + if (!config->digest_prefs.empty()) { + std::unique_ptr<char, Free<char>> digest_prefs( + strdup(config->digest_prefs.c_str())); + std::vector<int> digest_list; + + for (;;) { + char *token = + strtok(digest_list.empty() ? digest_prefs.get() : nullptr, ","); + if (token == nullptr) { + break; + } + + digest_list.push_back(EVP_MD_type(EVP_get_digestbyname(token))); + } + + if (!SSL_set_private_key_digest_prefs(ssl, digest_list.data(), + digest_list.size())) { + return false; + } + } + + if (!config->signing_prefs.empty()) { + std::vector<uint16_t> u16s(config->signing_prefs.begin(), + config->signing_prefs.end()); + if (!SSL_set_signing_algorithm_prefs(ssl, u16s.data(), u16s.size())) { + return false; + } + } + + if (!config->key_file.empty()) { + *out_pkey = LoadPrivateKey(config->key_file.c_str()); + if (!*out_pkey) { + return false; + } + } + if (!config->cert_file.empty() && + !LoadCertificate(out_x509, out_chain, config->cert_file.c_str())) { + return false; + } + if (!config->ocsp_response.empty() && + !SSL_set_ocsp_response(ssl, (const uint8_t *)config->ocsp_response.data(), + config->ocsp_response.size())) { + return false; + } + return true; +} + +static bool InstallCertificate(SSL *ssl) { + bssl::UniquePtr<X509> x509; + bssl::UniquePtr<STACK_OF(X509)> chain; + bssl::UniquePtr<EVP_PKEY> pkey; + if (!GetCertificate(ssl, &x509, &chain, &pkey)) { + return false; + } + + if (pkey) { + TestState *test_state = GetTestState(ssl); + const TestConfig *config = GetTestConfig(ssl); + if (config->async) { + test_state->private_key = std::move(pkey); + SSL_set_private_key_method(ssl, &g_async_private_key_method); + } else if (!SSL_use_PrivateKey(ssl, pkey.get())) { + return false; + } + } + + if (x509 && !SSL_use_certificate(ssl, x509.get())) { + return false; + } + + if (sk_X509_num(chain.get()) > 0 && + !SSL_set1_chain(ssl, chain.get())) { + return false; + } + + return true; +} + +static int SelectCertificateCallback(const SSL_CLIENT_HELLO *client_hello) { + const TestConfig *config = GetTestConfig(client_hello->ssl); + GetTestState(client_hello->ssl)->early_callback_called = true; + + if (!config->expected_server_name.empty()) { + const uint8_t *extension_data; + size_t extension_len; + CBS extension, server_name_list, host_name; + uint8_t name_type; + + if (!SSL_early_callback_ctx_extension_get( + client_hello, TLSEXT_TYPE_server_name, &extension_data, + &extension_len)) { + fprintf(stderr, "Could not find server_name extension.\n"); + return -1; + } + + CBS_init(&extension, extension_data, extension_len); + if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) || + CBS_len(&extension) != 0 || + !CBS_get_u8(&server_name_list, &name_type) || + name_type != TLSEXT_NAMETYPE_host_name || + !CBS_get_u16_length_prefixed(&server_name_list, &host_name) || + CBS_len(&server_name_list) != 0) { + fprintf(stderr, "Could not decode server_name extension.\n"); + return -1; + } + + if (!CBS_mem_equal(&host_name, + (const uint8_t*)config->expected_server_name.data(), + config->expected_server_name.size())) { + fprintf(stderr, "Server name mismatch.\n"); + } + } + + if (config->fail_early_callback) { + return -1; + } + + // Install the certificate in the early callback. + if (config->use_early_callback) { + bool early_callback_ready = + GetTestState(client_hello->ssl)->early_callback_ready; + if (config->async && !early_callback_ready) { + // Install the certificate asynchronously. + return 0; + } + if (!InstallCertificate(client_hello->ssl)) { + return -1; + } + } + return 1; +} + +static bool CheckCertificateRequest(SSL *ssl) { + const TestConfig *config = GetTestConfig(ssl); + + if (!config->expected_certificate_types.empty()) { + const uint8_t *certificate_types; + size_t certificate_types_len = + SSL_get0_certificate_types(ssl, &certificate_types); + if (certificate_types_len != config->expected_certificate_types.size() || + OPENSSL_memcmp(certificate_types, + config->expected_certificate_types.data(), + certificate_types_len) != 0) { + fprintf(stderr, "certificate types mismatch\n"); + return false; + } + } + + if (!config->expected_client_ca_list.empty()) { + bssl::UniquePtr<STACK_OF(X509_NAME)> expected = + DecodeHexX509Names(config->expected_client_ca_list); + const size_t num_expected = sk_X509_NAME_num(expected.get()); + + const STACK_OF(X509_NAME) *received = SSL_get_client_CA_list(ssl); + const size_t num_received = sk_X509_NAME_num(received); + + if (num_received != num_expected) { + fprintf(stderr, "expected %u names in CertificateRequest but got %u\n", + static_cast<unsigned>(num_expected), + static_cast<unsigned>(num_received)); + return false; + } + + for (size_t i = 0; i < num_received; i++) { + if (X509_NAME_cmp(sk_X509_NAME_value(received, i), + sk_X509_NAME_value(expected.get(), i)) != 0) { + fprintf(stderr, "names in CertificateRequest differ at index #%d\n", + static_cast<unsigned>(i)); + return false; + } + } + + STACK_OF(CRYPTO_BUFFER) *buffers = SSL_get0_server_requested_CAs(ssl); + if (sk_CRYPTO_BUFFER_num(buffers) != num_received) { + fprintf(stderr, + "Mismatch between SSL_get_server_requested_CAs and " + "SSL_get_client_CA_list.\n"); + return false; + } + } + + return true; +} + +static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) { + if (!CheckCertificateRequest(ssl)) { + return -1; + } + + if (GetTestConfig(ssl)->async && !GetTestState(ssl)->cert_ready) { + return -1; + } + + bssl::UniquePtr<X509> x509; + bssl::UniquePtr<STACK_OF(X509)> chain; + bssl::UniquePtr<EVP_PKEY> pkey; + if (!GetCertificate(ssl, &x509, &chain, &pkey)) { + return -1; + } + + // Return zero for no certificate. + if (!x509) { + return 0; + } + + // Chains and asynchronous private keys are not supported with client_cert_cb. + *out_x509 = x509.release(); + *out_pkey = pkey.release(); + return 1; +} + +static int CertCallback(SSL *ssl, void *arg) { + const TestConfig *config = GetTestConfig(ssl); + + // Check the CertificateRequest metadata is as expected. + if (!SSL_is_server(ssl) && !CheckCertificateRequest(ssl)) { + return -1; + } + + if (config->fail_cert_callback) { + return 0; + } + + // The certificate will be installed via other means. + if (!config->async || config->use_early_callback) { + return 1; + } + + if (!GetTestState(ssl)->cert_ready) { + return -1; + } + if (!InstallCertificate(ssl)) { + return 0; + } + return 1; +} + +static int VerifySucceed(X509_STORE_CTX *store_ctx, void *arg) { + SSL* ssl = (SSL*)X509_STORE_CTX_get_ex_data(store_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + const TestConfig *config = GetTestConfig(ssl); + + if (!config->expected_ocsp_response.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_ocsp_response(ssl, &data, &len); + if (len == 0) { + fprintf(stderr, "OCSP response not available in verify callback\n"); + return 0; + } + } + + return 1; +} + +static int VerifyFail(X509_STORE_CTX *store_ctx, void *arg) { + store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION; + return 0; +} + +static int NextProtosAdvertisedCallback(SSL *ssl, const uint8_t **out, + unsigned int *out_len, void *arg) { + const TestConfig *config = GetTestConfig(ssl); + if (config->advertise_npn.empty()) { + return SSL_TLSEXT_ERR_NOACK; + } + + *out = (const uint8_t*)config->advertise_npn.data(); + *out_len = config->advertise_npn.size(); + return SSL_TLSEXT_ERR_OK; +} + +static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen, + const uint8_t* in, unsigned inlen, void* arg) { + const TestConfig *config = GetTestConfig(ssl); + if (config->select_next_proto.empty()) { + return SSL_TLSEXT_ERR_NOACK; + } + + *out = (uint8_t*)config->select_next_proto.data(); + *outlen = config->select_next_proto.size(); + return SSL_TLSEXT_ERR_OK; +} + +static int AlpnSelectCallback(SSL* ssl, const uint8_t** out, uint8_t* outlen, + const uint8_t* in, unsigned inlen, void* arg) { + if (GetTestState(ssl)->alpn_select_done) { + fprintf(stderr, "AlpnSelectCallback called after completion.\n"); + exit(1); + } + + GetTestState(ssl)->alpn_select_done = true; + + const TestConfig *config = GetTestConfig(ssl); + if (config->decline_alpn) { + return SSL_TLSEXT_ERR_NOACK; + } + + if (!config->expected_advertised_alpn.empty() && + (config->expected_advertised_alpn.size() != inlen || + OPENSSL_memcmp(config->expected_advertised_alpn.data(), in, inlen) != + 0)) { + fprintf(stderr, "bad ALPN select callback inputs\n"); + exit(1); + } + + *out = (const uint8_t*)config->select_alpn.data(); + *outlen = config->select_alpn.size(); + return SSL_TLSEXT_ERR_OK; +} + +static unsigned PskClientCallback(SSL *ssl, const char *hint, + char *out_identity, + unsigned max_identity_len, + uint8_t *out_psk, unsigned max_psk_len) { + const TestConfig *config = GetTestConfig(ssl); + + if (config->psk_identity.empty()) { + if (hint != nullptr) { + fprintf(stderr, "Server PSK hint was non-null.\n"); + return 0; + } + } else if (hint == nullptr || + strcmp(hint, config->psk_identity.c_str()) != 0) { + fprintf(stderr, "Server PSK hint did not match.\n"); + return 0; + } + + // Account for the trailing '\0' for the identity. + if (config->psk_identity.size() >= max_identity_len || + config->psk.size() > max_psk_len) { + fprintf(stderr, "PSK buffers too small\n"); + return 0; + } + + BUF_strlcpy(out_identity, config->psk_identity.c_str(), + max_identity_len); + OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size()); + return config->psk.size(); +} + +static unsigned PskServerCallback(SSL *ssl, const char *identity, + uint8_t *out_psk, unsigned max_psk_len) { + const TestConfig *config = GetTestConfig(ssl); + + if (strcmp(identity, config->psk_identity.c_str()) != 0) { + fprintf(stderr, "Client PSK identity did not match.\n"); + return 0; + } + + if (config->psk.size() > max_psk_len) { + fprintf(stderr, "PSK buffers too small\n"); + return 0; + } + + OPENSSL_memcpy(out_psk, config->psk.data(), config->psk.size()); + return config->psk.size(); +} + +static timeval g_clock; + +static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) { + *out_clock = g_clock; +} + +static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) { + *out_pkey = GetTestState(ssl)->channel_id.release(); +} + +static SSL_SESSION *GetSessionCallback(SSL *ssl, uint8_t *data, int len, + int *copy) { + TestState *async_state = GetTestState(ssl); + if (async_state->session) { + *copy = 0; + return async_state->session.release(); + } else if (async_state->pending_session) { + return SSL_magic_pending_session_ptr(); + } else { + return NULL; + } +} + +static int DDoSCallback(const SSL_CLIENT_HELLO *client_hello) { + const TestConfig *config = GetTestConfig(client_hello->ssl); + static int callback_num = 0; + + callback_num++; + if (config->fail_ddos_callback || + (config->fail_second_ddos_callback && callback_num == 2)) { + return 0; + } + return 1; +} + +static void InfoCallback(const SSL *ssl, int type, int val) { + if (type == SSL_CB_HANDSHAKE_DONE) { + if (GetTestConfig(ssl)->handshake_never_done) { + fprintf(stderr, "Handshake unexpectedly completed.\n"); + // Abort before any expected error code is printed, to ensure the overall + // test fails. + abort(); + } + GetTestState(ssl)->handshake_done = true; + + // Callbacks may be called again on a new handshake. + GetTestState(ssl)->ticket_decrypt_done = false; + GetTestState(ssl)->alpn_select_done = false; + } +} + +static int NewSessionCallback(SSL *ssl, SSL_SESSION *session) { + GetTestState(ssl)->got_new_session = true; + GetTestState(ssl)->new_session.reset(session); + return 1; +} + +static int TicketKeyCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv, + EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx, + int encrypt) { + if (!encrypt) { + if (GetTestState(ssl)->ticket_decrypt_done) { + fprintf(stderr, "TicketKeyCallback called after completion.\n"); + return -1; + } + + GetTestState(ssl)->ticket_decrypt_done = true; + } + + // This is just test code, so use the all-zeros key. + static const uint8_t kZeros[16] = {0}; + + if (encrypt) { + OPENSSL_memcpy(key_name, kZeros, sizeof(kZeros)); + RAND_bytes(iv, 16); + } else if (OPENSSL_memcmp(key_name, kZeros, 16) != 0) { + return 0; + } + + if (!HMAC_Init_ex(hmac_ctx, kZeros, sizeof(kZeros), EVP_sha256(), NULL) || + !EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, kZeros, iv, encrypt)) { + return -1; + } + + if (!encrypt) { + return GetTestConfig(ssl)->renew_ticket ? 2 : 1; + } + return 1; +} + +// kCustomExtensionValue is the extension value that the custom extension +// callbacks will add. +static const uint16_t kCustomExtensionValue = 1234; +static void *const kCustomExtensionAddArg = + reinterpret_cast<void *>(kCustomExtensionValue); +static void *const kCustomExtensionParseArg = + reinterpret_cast<void *>(kCustomExtensionValue + 1); +static const char kCustomExtensionContents[] = "custom extension"; + +static int CustomExtensionAddCallback(SSL *ssl, unsigned extension_value, + const uint8_t **out, size_t *out_len, + int *out_alert_value, void *add_arg) { + if (extension_value != kCustomExtensionValue || + add_arg != kCustomExtensionAddArg) { + abort(); + } + + if (GetTestConfig(ssl)->custom_extension_skip) { + return 0; + } + if (GetTestConfig(ssl)->custom_extension_fail_add) { + return -1; + } + + *out = reinterpret_cast<const uint8_t*>(kCustomExtensionContents); + *out_len = sizeof(kCustomExtensionContents) - 1; + + return 1; +} + +static void CustomExtensionFreeCallback(SSL *ssl, unsigned extension_value, + const uint8_t *out, void *add_arg) { + if (extension_value != kCustomExtensionValue || + add_arg != kCustomExtensionAddArg || + out != reinterpret_cast<const uint8_t *>(kCustomExtensionContents)) { + abort(); + } +} + +static int CustomExtensionParseCallback(SSL *ssl, unsigned extension_value, + const uint8_t *contents, + size_t contents_len, + int *out_alert_value, void *parse_arg) { + if (extension_value != kCustomExtensionValue || + parse_arg != kCustomExtensionParseArg) { + abort(); + } + + if (contents_len != sizeof(kCustomExtensionContents) - 1 || + OPENSSL_memcmp(contents, kCustomExtensionContents, contents_len) != 0) { + *out_alert_value = SSL_AD_DECODE_ERROR; + return 0; + } + + return 1; +} + +static int ServerNameCallback(SSL *ssl, int *out_alert, void *arg) { + // SNI must be accessible from the SNI callback. + const TestConfig *config = GetTestConfig(ssl); + const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name == nullptr || + std::string(server_name) != config->expected_server_name) { + fprintf(stderr, "servername mismatch (got %s; want %s)\n", server_name, + config->expected_server_name.c_str()); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } + + return SSL_TLSEXT_ERR_OK; +} + +// Connect returns a new socket connected to localhost on |port| or -1 on +// error. +static int Connect(uint16_t port) { + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) { + PrintSocketError("socket"); + return -1; + } + int nodelay = 1; + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + reinterpret_cast<const char*>(&nodelay), sizeof(nodelay)) != 0) { + PrintSocketError("setsockopt"); + closesocket(sock); + return -1; + } + sockaddr_in sin; + OPENSSL_memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + if (!inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr)) { + PrintSocketError("inet_pton"); + closesocket(sock); + return -1; + } + if (connect(sock, reinterpret_cast<const sockaddr*>(&sin), + sizeof(sin)) != 0) { + PrintSocketError("connect"); + closesocket(sock); + return -1; + } + return sock; +} + +class SocketCloser { + public: + explicit SocketCloser(int sock) : sock_(sock) {} + ~SocketCloser() { + // Half-close and drain the socket before releasing it. This seems to be + // necessary for graceful shutdown on Windows. It will also avoid write + // failures in the test runner. +#if defined(OPENSSL_WINDOWS) + shutdown(sock_, SD_SEND); +#else + shutdown(sock_, SHUT_WR); +#endif + while (true) { + char buf[1024]; + if (recv(sock_, buf, sizeof(buf), 0) <= 0) { + break; + } + } + closesocket(sock_); + } + + private: + const int sock_; +}; + +static bssl::UniquePtr<SSL_CTX> SetupCtx(const TestConfig *config) { + bssl::UniquePtr<SSL_CTX> ssl_ctx(SSL_CTX_new( + config->is_dtls ? DTLS_method() : TLS_method())); + if (!ssl_ctx) { + return nullptr; + } + + SSL_CTX_set0_buffer_pool(ssl_ctx.get(), g_pool); + + // Enable TLS 1.3 for tests. + if (!config->is_dtls && + !SSL_CTX_set_max_proto_version(ssl_ctx.get(), TLS1_3_VERSION)) { + return nullptr; + } + + std::string cipher_list = "ALL"; + if (!config->cipher.empty()) { + cipher_list = config->cipher; + SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE); + } + if (!SSL_CTX_set_strict_cipher_list(ssl_ctx.get(), cipher_list.c_str())) { + return nullptr; + } + + bssl::UniquePtr<DH> dh(DH_get_2048_256(NULL)); + if (!dh) { + return nullptr; + } + + if (config->use_sparse_dh_prime) { + // This prime number is 2^1024 + 643 – a value just above a power of two. + // Because of its form, values modulo it are essentially certain to be one + // byte shorter. This is used to test padding of these values. + if (BN_hex2bn( + &dh->p, + "1000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000028" + "3") == 0 || + !BN_set_word(dh->g, 2)) { + return nullptr; + } + BN_free(dh->q); + dh->q = NULL; + dh->priv_length = 0; + } + + if (!SSL_CTX_set_tmp_dh(ssl_ctx.get(), dh.get())) { + return nullptr; + } + + if (config->async && config->is_server) { + // Disable the internal session cache. To test asynchronous session lookup, + // we use an external session cache. + SSL_CTX_set_session_cache_mode( + ssl_ctx.get(), SSL_SESS_CACHE_BOTH | SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_get_cb(ssl_ctx.get(), GetSessionCallback); + } else { + SSL_CTX_set_session_cache_mode(ssl_ctx.get(), SSL_SESS_CACHE_BOTH); + } + + SSL_CTX_set_select_certificate_cb(ssl_ctx.get(), SelectCertificateCallback); + + if (config->use_old_client_cert_callback) { + SSL_CTX_set_client_cert_cb(ssl_ctx.get(), ClientCertCallback); + } + + SSL_CTX_set_next_protos_advertised_cb( + ssl_ctx.get(), NextProtosAdvertisedCallback, NULL); + if (!config->select_next_proto.empty()) { + SSL_CTX_set_next_proto_select_cb(ssl_ctx.get(), NextProtoSelectCallback, + NULL); + } + + if (!config->select_alpn.empty() || config->decline_alpn) { + SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL); + } + + SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback); + + SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback); + + SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback); + SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback); + + if (config->use_ticket_callback) { + SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback); + } + + if (config->enable_client_custom_extension && + !SSL_CTX_add_client_custom_ext( + ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback, + CustomExtensionFreeCallback, kCustomExtensionAddArg, + CustomExtensionParseCallback, kCustomExtensionParseArg)) { + return nullptr; + } + + if (config->enable_server_custom_extension && + !SSL_CTX_add_server_custom_ext( + ssl_ctx.get(), kCustomExtensionValue, CustomExtensionAddCallback, + CustomExtensionFreeCallback, kCustomExtensionAddArg, + CustomExtensionParseCallback, kCustomExtensionParseArg)) { + return nullptr; + } + + if (config->verify_fail) { + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifyFail, NULL); + } else { + SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifySucceed, NULL); + } + + if (!config->signed_cert_timestamps.empty() && + !SSL_CTX_set_signed_cert_timestamp_list( + ssl_ctx.get(), (const uint8_t *)config->signed_cert_timestamps.data(), + config->signed_cert_timestamps.size())) { + return nullptr; + } + + if (!config->use_client_ca_list.empty()) { + if (config->use_client_ca_list == "<NULL>") { + SSL_CTX_set_client_CA_list(ssl_ctx.get(), nullptr); + } else { + bssl::UniquePtr<STACK_OF(X509_NAME)> names = + DecodeHexX509Names(config->use_client_ca_list); + SSL_CTX_set_client_CA_list(ssl_ctx.get(), names.release()); + } + } + + if (config->enable_grease) { + SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1); + } + + if (!config->expected_server_name.empty()) { + SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback); + } + + if (!config->ticket_key.empty() && + !SSL_CTX_set_tlsext_ticket_keys(ssl_ctx.get(), config->ticket_key.data(), + config->ticket_key.size())) { + return nullptr; + } + + if (config->enable_early_data) { + SSL_CTX_set_early_data_enabled(ssl_ctx.get(), 1); + } + + return ssl_ctx; +} + +// RetryAsync is called after a failed operation on |ssl| with return code +// |ret|. If the operation should be retried, it simulates one asynchronous +// event and returns true. Otherwise it returns false. +static bool RetryAsync(SSL *ssl, int ret) { + // No error; don't retry. + if (ret >= 0) { + return false; + } + + TestState *test_state = GetTestState(ssl); + assert(GetTestConfig(ssl)->async); + + if (test_state->packeted_bio != nullptr && + PacketedBioAdvanceClock(test_state->packeted_bio)) { + // The DTLS retransmit logic silently ignores write failures. So the test + // may progress, allow writes through synchronously. + AsyncBioEnforceWriteQuota(test_state->async_bio, false); + int timeout_ret = DTLSv1_handle_timeout(ssl); + AsyncBioEnforceWriteQuota(test_state->async_bio, true); + + if (timeout_ret < 0) { + fprintf(stderr, "Error retransmitting.\n"); + return false; + } + return true; + } + + // See if we needed to read or write more. If so, allow one byte through on + // the appropriate end to maximally stress the state machine. + switch (SSL_get_error(ssl, ret)) { + case SSL_ERROR_WANT_READ: + AsyncBioAllowRead(test_state->async_bio, 1); + return true; + case SSL_ERROR_WANT_WRITE: + AsyncBioAllowWrite(test_state->async_bio, 1); + return true; + case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: { + bssl::UniquePtr<EVP_PKEY> pkey = + LoadPrivateKey(GetTestConfig(ssl)->send_channel_id); + if (!pkey) { + return false; + } + test_state->channel_id = std::move(pkey); + return true; + } + case SSL_ERROR_WANT_X509_LOOKUP: + test_state->cert_ready = true; + return true; + case SSL_ERROR_PENDING_SESSION: + test_state->session = std::move(test_state->pending_session); + return true; + case SSL_ERROR_PENDING_CERTIFICATE: + test_state->early_callback_ready = true; + return true; + case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION: + test_state->private_key_retries++; + return true; + default: + return false; + } +} + +// DoRead reads from |ssl|, resolving any asynchronous operations. It returns +// the result value of the final |SSL_read| call. +static int DoRead(SSL *ssl, uint8_t *out, size_t max_out) { + const TestConfig *config = GetTestConfig(ssl); + TestState *test_state = GetTestState(ssl); + int ret; + do { + if (config->async) { + // The DTLS retransmit logic silently ignores write failures. So the test + // may progress, allow writes through synchronously. |SSL_read| may + // trigger a retransmit, so disconnect the write quota. + AsyncBioEnforceWriteQuota(test_state->async_bio, false); + } + ret = config->peek_then_read ? SSL_peek(ssl, out, max_out) + : SSL_read(ssl, out, max_out); + if (config->async) { + AsyncBioEnforceWriteQuota(test_state->async_bio, true); + } + + // Run the exporter after each read. This is to test that the exporter fails + // during a renegotiation. + if (config->use_exporter_between_reads) { + uint8_t buf; + if (!SSL_export_keying_material(ssl, &buf, 1, NULL, 0, NULL, 0, 0)) { + fprintf(stderr, "failed to export keying material\n"); + return -1; + } + } + } while (config->async && RetryAsync(ssl, ret)); + + if (config->peek_then_read && ret > 0) { + std::unique_ptr<uint8_t[]> buf(new uint8_t[static_cast<size_t>(ret)]); + + // SSL_peek should synchronously return the same data. + int ret2 = SSL_peek(ssl, buf.get(), ret); + if (ret2 != ret || + OPENSSL_memcmp(buf.get(), out, ret) != 0) { + fprintf(stderr, "First and second SSL_peek did not match.\n"); + return -1; + } + + // SSL_read should synchronously return the same data and consume it. + ret2 = SSL_read(ssl, buf.get(), ret); + if (ret2 != ret || + OPENSSL_memcmp(buf.get(), out, ret) != 0) { + fprintf(stderr, "SSL_peek and SSL_read did not match.\n"); + return -1; + } + } + + return ret; +} + +// WriteAll writes |in_len| bytes from |in| to |ssl|, resolving any asynchronous +// operations. It returns the result of the final |SSL_write| call. +static int WriteAll(SSL *ssl, const uint8_t *in, size_t in_len) { + const TestConfig *config = GetTestConfig(ssl); + int ret; + do { + ret = SSL_write(ssl, in, in_len); + if (ret > 0) { + in += ret; + in_len -= ret; + } + } while ((config->async && RetryAsync(ssl, ret)) || (ret > 0 && in_len > 0)); + return ret; +} + +// DoShutdown calls |SSL_shutdown|, resolving any asynchronous operations. It +// returns the result of the final |SSL_shutdown| call. +static int DoShutdown(SSL *ssl) { + const TestConfig *config = GetTestConfig(ssl); + int ret; + do { + ret = SSL_shutdown(ssl); + } while (config->async && RetryAsync(ssl, ret)); + return ret; +} + +// DoSendFatalAlert calls |SSL_send_fatal_alert|, resolving any asynchronous +// operations. It returns the result of the final |SSL_send_fatal_alert| call. +static int DoSendFatalAlert(SSL *ssl, uint8_t alert) { + const TestConfig *config = GetTestConfig(ssl); + int ret; + do { + ret = SSL_send_fatal_alert(ssl, alert); + } while (config->async && RetryAsync(ssl, ret)); + return ret; +} + +static uint16_t GetProtocolVersion(const SSL *ssl) { + uint16_t version = SSL_version(ssl); + if (!SSL_is_dtls(ssl)) { + return version; + } + return 0x0201 + ~version; +} + +// CheckHandshakeProperties checks, immediately after |ssl| completes its +// initial handshake (or False Starts), whether all the properties are +// consistent with the test configuration and invariants. +static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) { + const TestConfig *config = GetTestConfig(ssl); + + if (SSL_get_current_cipher(ssl) == nullptr) { + fprintf(stderr, "null cipher after handshake\n"); + return false; + } + + if (is_resume && + (!!SSL_session_reused(ssl) == config->expect_session_miss)) { + fprintf(stderr, "session was%s reused\n", + SSL_session_reused(ssl) ? "" : " not"); + return false; + } + + bool expect_handshake_done = is_resume || !config->false_start; + if (expect_handshake_done != GetTestState(ssl)->handshake_done) { + fprintf(stderr, "handshake was%s completed\n", + GetTestState(ssl)->handshake_done ? "" : " not"); + return false; + } + + if (expect_handshake_done && !config->is_server) { + bool expect_new_session = + !config->expect_no_session && + (!SSL_session_reused(ssl) || config->expect_ticket_renewal) && + // Session tickets are sent post-handshake in TLS 1.3. + GetProtocolVersion(ssl) < TLS1_3_VERSION; + if (expect_new_session != GetTestState(ssl)->got_new_session) { + fprintf(stderr, + "new session was%s cached, but we expected the opposite\n", + GetTestState(ssl)->got_new_session ? "" : " not"); + return false; + } + } + + if (!is_resume) { + if (config->expect_session_id && !GetTestState(ssl)->got_new_session) { + fprintf(stderr, "session was not cached on the server.\n"); + return false; + } + if (config->expect_no_session_id && GetTestState(ssl)->got_new_session) { + fprintf(stderr, "session was unexpectedly cached on the server.\n"); + return false; + } + } + + if (config->is_server && !GetTestState(ssl)->early_callback_called) { + fprintf(stderr, "early callback not called\n"); + return false; + } + + if (!config->expected_server_name.empty()) { + const char *server_name = + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name == nullptr || + server_name != config->expected_server_name) { + fprintf(stderr, "servername mismatch (got %s; want %s)\n", + server_name, config->expected_server_name.c_str()); + return false; + } + } + + if (!config->expected_next_proto.empty()) { + const uint8_t *next_proto; + unsigned next_proto_len; + SSL_get0_next_proto_negotiated(ssl, &next_proto, &next_proto_len); + if (next_proto_len != config->expected_next_proto.size() || + OPENSSL_memcmp(next_proto, config->expected_next_proto.data(), + next_proto_len) != 0) { + fprintf(stderr, "negotiated next proto mismatch\n"); + return false; + } + } + + if (!config->expected_alpn.empty()) { + const uint8_t *alpn_proto; + unsigned alpn_proto_len; + SSL_get0_alpn_selected(ssl, &alpn_proto, &alpn_proto_len); + if (alpn_proto_len != config->expected_alpn.size() || + OPENSSL_memcmp(alpn_proto, config->expected_alpn.data(), + alpn_proto_len) != 0) { + fprintf(stderr, "negotiated alpn proto mismatch\n"); + return false; + } + } + + if (!config->expected_channel_id.empty()) { + uint8_t channel_id[64]; + if (!SSL_get_tls_channel_id(ssl, channel_id, sizeof(channel_id))) { + fprintf(stderr, "no channel id negotiated\n"); + return false; + } + if (config->expected_channel_id.size() != 64 || + OPENSSL_memcmp(config->expected_channel_id.data(), channel_id, 64) != + 0) { + fprintf(stderr, "channel id mismatch\n"); + return false; + } + } + + if (config->expect_extended_master_secret && !SSL_get_extms_support(ssl)) { + fprintf(stderr, "No EMS for connection when expected\n"); + return false; + } + + if (config->expect_secure_renegotiation && + !SSL_get_secure_renegotiation_support(ssl)) { + fprintf(stderr, "No secure renegotiation for connection when expected\n"); + return false; + } + + if (config->expect_no_secure_renegotiation && + SSL_get_secure_renegotiation_support(ssl)) { + fprintf(stderr, + "Secure renegotiation unexpectedly negotiated for connection\n"); + return false; + } + + if (!config->expected_ocsp_response.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_ocsp_response(ssl, &data, &len); + if (config->expected_ocsp_response.size() != len || + OPENSSL_memcmp(config->expected_ocsp_response.data(), data, len) != 0) { + fprintf(stderr, "OCSP response mismatch\n"); + return false; + } + } + + if (!config->expected_signed_cert_timestamps.empty()) { + const uint8_t *data; + size_t len; + SSL_get0_signed_cert_timestamp_list(ssl, &data, &len); + if (config->expected_signed_cert_timestamps.size() != len || + OPENSSL_memcmp(config->expected_signed_cert_timestamps.data(), data, + len) != 0) { + fprintf(stderr, "SCT list mismatch\n"); + return false; + } + } + + if (config->expect_verify_result) { + int expected_verify_result = config->verify_fail ? + X509_V_ERR_APPLICATION_VERIFICATION : + X509_V_OK; + + if (SSL_get_verify_result(ssl) != expected_verify_result) { + fprintf(stderr, "Wrong certificate verification result\n"); + return false; + } + } + + if (config->expect_peer_signature_algorithm != 0 && + config->expect_peer_signature_algorithm != + SSL_get_peer_signature_algorithm(ssl)) { + fprintf(stderr, "Peer signature algorithm was %04x, wanted %04x.\n", + SSL_get_peer_signature_algorithm(ssl), + config->expect_peer_signature_algorithm); + return false; + } + + int expect_curve_id = config->expect_curve_id; + if (is_resume && config->expect_resume_curve_id != 0) { + expect_curve_id = config->expect_resume_curve_id; + } + if (expect_curve_id != 0) { + uint16_t curve_id = SSL_get_curve_id(ssl); + if (static_cast<uint16_t>(expect_curve_id) != curve_id) { + fprintf(stderr, "curve_id was %04x, wanted %04x\n", curve_id, + static_cast<uint16_t>(expect_curve_id)); + return false; + } + } + + uint16_t cipher_id = + static_cast<uint16_t>(SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))); + if (config->expect_cipher_aes != 0 && + EVP_has_aes_hardware() && + static_cast<uint16_t>(config->expect_cipher_aes) != cipher_id) { + fprintf(stderr, "Cipher ID was %04x, wanted %04x (has AES hardware)\n", + cipher_id, static_cast<uint16_t>(config->expect_cipher_aes)); + return false; + } + + if (config->expect_cipher_no_aes != 0 && + !EVP_has_aes_hardware() && + static_cast<uint16_t>(config->expect_cipher_no_aes) != cipher_id) { + fprintf(stderr, "Cipher ID was %04x, wanted %04x (no AES hardware)\n", + cipher_id, static_cast<uint16_t>(config->expect_cipher_no_aes)); + return false; + } + + + if (!config->psk.empty()) { + if (SSL_get_peer_cert_chain(ssl) != nullptr) { + fprintf(stderr, "Received peer certificate on a PSK cipher.\n"); + return false; + } + } else if (!config->is_server || config->require_any_client_certificate) { + if (SSL_get_peer_cert_chain(ssl) == nullptr) { + fprintf(stderr, "Received no peer certificate but expected one.\n"); + return false; + } + } + + if (!config->expect_peer_cert_file.empty()) { + bssl::UniquePtr<X509> expect_leaf; + bssl::UniquePtr<STACK_OF(X509)> expect_chain; + if (!LoadCertificate(&expect_leaf, &expect_chain, + config->expect_peer_cert_file)) { + return false; + } + + // For historical reasons, clients report a chain with a leaf and servers + // without. + if (!config->is_server) { + if (!sk_X509_insert(expect_chain.get(), expect_leaf.get(), 0)) { + return false; + } + X509_up_ref(expect_leaf.get()); // sk_X509_push takes ownership. + } + + bssl::UniquePtr<X509> leaf(SSL_get_peer_certificate(ssl)); + STACK_OF(X509) *chain = SSL_get_peer_cert_chain(ssl); + if (X509_cmp(leaf.get(), expect_leaf.get()) != 0) { + fprintf(stderr, "Received a different leaf certificate than expected.\n"); + return false; + } + + if (sk_X509_num(chain) != sk_X509_num(expect_chain.get())) { + fprintf(stderr, "Received a chain of length %zu instead of %zu.\n", + sk_X509_num(chain), sk_X509_num(expect_chain.get())); + return false; + } + + for (size_t i = 0; i < sk_X509_num(chain); i++) { + if (X509_cmp(sk_X509_value(chain, i), + sk_X509_value(expect_chain.get(), i)) != 0) { + fprintf(stderr, "Chain certificate %zu did not match.\n", + i + 1); + return false; + } + } + } + + bool expected_sha256_client_cert = config->expect_sha256_client_cert_initial; + if (is_resume) { + expected_sha256_client_cert = config->expect_sha256_client_cert_resume; + } + + if (SSL_get_session(ssl)->peer_sha256_valid != expected_sha256_client_cert) { + fprintf(stderr, + "Unexpected SHA-256 client cert state: expected:%d is_resume:%d.\n", + expected_sha256_client_cert, is_resume); + return false; + } + + if (expected_sha256_client_cert && + SSL_get_session(ssl)->certs != nullptr) { + fprintf(stderr, "Have both client cert and SHA-256 hash: is_resume:%d.\n", + is_resume); + return false; + } + + if (is_resume && config->expect_ticket_age_skew != 0 && + SSL_get_ticket_age_skew(ssl) != config->expect_ticket_age_skew) { + fprintf(stderr, "Ticket age skew was %" PRId32 ", wanted %d\n", + SSL_get_ticket_age_skew(ssl), config->expect_ticket_age_skew); + return false; + } + + return true; +} + +// DoExchange runs a test SSL exchange against the peer. On success, it returns +// true and sets |*out_session| to the negotiated SSL session. If the test is a +// resumption attempt, |is_resume| is true and |session| is the session from the +// previous exchange. +static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, + SSL_CTX *ssl_ctx, const TestConfig *config, + bool is_resume, SSL_SESSION *session) { + bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx)); + if (!ssl) { + return false; + } + + if (!SetTestConfig(ssl.get(), config) || + !SetTestState(ssl.get(), std::unique_ptr<TestState>(new TestState))) { + return false; + } + + if (config->fallback_scsv && + !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) { + return false; + } + // Install the certificate synchronously if nothing else will handle it. + if (!config->use_early_callback && + !config->use_old_client_cert_callback && + !config->async && + !InstallCertificate(ssl.get())) { + return false; + } + if (!config->use_old_client_cert_callback) { + SSL_set_cert_cb(ssl.get(), CertCallback, nullptr); + } + if (config->require_any_client_certificate) { + SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + NULL); + } + if (config->verify_peer) { + SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL); + } + if (config->false_start) { + SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START); + } + if (config->cbc_record_splitting) { + SSL_set_mode(ssl.get(), SSL_MODE_CBC_RECORD_SPLITTING); + } + if (config->partial_write) { + SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_PARTIAL_WRITE); + } + if (config->no_tls13) { + SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_3); + } + if (config->no_tls12) { + SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_2); + } + if (config->no_tls11) { + SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1_1); + } + if (config->no_tls1) { + SSL_set_options(ssl.get(), SSL_OP_NO_TLSv1); + } + if (config->no_ssl3) { + SSL_set_options(ssl.get(), SSL_OP_NO_SSLv3); + } + if (!config->expected_channel_id.empty() || + config->enable_channel_id) { + SSL_set_tls_channel_id_enabled(ssl.get(), 1); + } + if (!config->send_channel_id.empty()) { + SSL_set_tls_channel_id_enabled(ssl.get(), 1); + if (!config->async) { + // The async case will be supplied by |ChannelIdCallback|. + bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(config->send_channel_id); + if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) { + return false; + } + } + } + if (!config->host_name.empty() && + !SSL_set_tlsext_host_name(ssl.get(), config->host_name.c_str())) { + return false; + } + if (!config->advertise_alpn.empty() && + SSL_set_alpn_protos(ssl.get(), + (const uint8_t *)config->advertise_alpn.data(), + config->advertise_alpn.size()) != 0) { + return false; + } + if (!config->psk.empty()) { + SSL_set_psk_client_callback(ssl.get(), PskClientCallback); + SSL_set_psk_server_callback(ssl.get(), PskServerCallback); + } + if (!config->psk_identity.empty() && + !SSL_use_psk_identity_hint(ssl.get(), config->psk_identity.c_str())) { + return false; + } + if (!config->srtp_profiles.empty() && + !SSL_set_srtp_profiles(ssl.get(), config->srtp_profiles.c_str())) { + return false; + } + if (config->enable_ocsp_stapling) { + SSL_enable_ocsp_stapling(ssl.get()); + } + if (config->enable_signed_cert_timestamps) { + SSL_enable_signed_cert_timestamps(ssl.get()); + } + if (config->min_version != 0 && + !SSL_set_min_proto_version(ssl.get(), (uint16_t)config->min_version)) { + return false; + } + if (config->max_version != 0 && + !SSL_set_max_proto_version(ssl.get(), (uint16_t)config->max_version)) { + return false; + } + if (config->mtu != 0) { + SSL_set_options(ssl.get(), SSL_OP_NO_QUERY_MTU); + SSL_set_mtu(ssl.get(), config->mtu); + } + if (config->install_ddos_callback) { + SSL_CTX_set_dos_protection_cb(ssl_ctx, DDoSCallback); + } + if (config->renegotiate_once) { + SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_once); + } + if (config->renegotiate_freely) { + SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely); + } + if (config->renegotiate_ignore) { + SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore); + } + if (!config->check_close_notify) { + SSL_set_quiet_shutdown(ssl.get(), 1); + } + if (config->p384_only) { + int nid = NID_secp384r1; + if (!SSL_set1_curves(ssl.get(), &nid, 1)) { + return false; + } + } + if (config->enable_all_curves) { + static const int kAllCurves[] = { + NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_X25519, + }; + if (!SSL_set1_curves(ssl.get(), kAllCurves, + OPENSSL_ARRAY_SIZE(kAllCurves))) { + return false; + } + } + if (config->initial_timeout_duration_ms > 0) { + DTLSv1_set_initial_timeout_duration(ssl.get(), + config->initial_timeout_duration_ms); + } + if (config->max_cert_list > 0) { + SSL_set_max_cert_list(ssl.get(), config->max_cert_list); + } + if (!is_resume && config->retain_only_sha256_client_cert_initial) { + SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1); + } + if (is_resume && config->retain_only_sha256_client_cert_resume) { + SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1); + } + if (config->max_send_fragment > 0) { + SSL_set_max_send_fragment(ssl.get(), config->max_send_fragment); + } + + int sock = Connect(config->port); + if (sock == -1) { + return false; + } + SocketCloser closer(sock); + + bssl::UniquePtr<BIO> bio(BIO_new_socket(sock, BIO_NOCLOSE)); + if (!bio) { + return false; + } + if (config->is_dtls) { + bssl::UniquePtr<BIO> packeted = PacketedBioCreate(&g_clock); + if (!packeted) { + return false; + } + GetTestState(ssl.get())->packeted_bio = packeted.get(); + BIO_push(packeted.get(), bio.release()); + bio = std::move(packeted); + } + if (config->async) { + bssl::UniquePtr<BIO> async_scoped = + config->is_dtls ? AsyncBioCreateDatagram() : AsyncBioCreate(); + if (!async_scoped) { + return false; + } + BIO_push(async_scoped.get(), bio.release()); + GetTestState(ssl.get())->async_bio = async_scoped.get(); + bio = std::move(async_scoped); + } + SSL_set_bio(ssl.get(), bio.get(), bio.get()); + bio.release(); // SSL_set_bio takes ownership. + + if (session != NULL) { + if (!config->is_server) { + if (SSL_set_session(ssl.get(), session) != 1) { + return false; + } + } else if (config->async) { + // The internal session cache is disabled, so install the session + // manually. + SSL_SESSION_up_ref(session); + GetTestState(ssl.get())->pending_session.reset(session); + } + } + + if (SSL_get_current_cipher(ssl.get()) != nullptr) { + fprintf(stderr, "non-null cipher before handshake\n"); + return false; + } + + int ret; + if (config->implicit_handshake) { + if (config->is_server) { + SSL_set_accept_state(ssl.get()); + } else { + SSL_set_connect_state(ssl.get()); + } + } else { + do { + if (config->is_server) { + ret = SSL_accept(ssl.get()); + } else { + ret = SSL_connect(ssl.get()); + } + } while (config->async && RetryAsync(ssl.get(), ret)); + if (ret != 1 || + !CheckHandshakeProperties(ssl.get(), is_resume)) { + return false; + } + + // Reset the state to assert later that the callback isn't called in + // renegotations. + GetTestState(ssl.get())->got_new_session = false; + } + + if (config->export_keying_material > 0) { + std::vector<uint8_t> result( + static_cast<size_t>(config->export_keying_material)); + if (!SSL_export_keying_material( + ssl.get(), result.data(), result.size(), + config->export_label.data(), config->export_label.size(), + reinterpret_cast<const uint8_t*>(config->export_context.data()), + config->export_context.size(), config->use_export_context)) { + fprintf(stderr, "failed to export keying material\n"); + return false; + } + if (WriteAll(ssl.get(), result.data(), result.size()) < 0) { + return false; + } + } + + if (config->tls_unique) { + uint8_t tls_unique[16]; + size_t tls_unique_len; + if (!SSL_get_tls_unique(ssl.get(), tls_unique, &tls_unique_len, + sizeof(tls_unique))) { + fprintf(stderr, "failed to get tls-unique\n"); + return false; + } + + if (tls_unique_len != 12) { + fprintf(stderr, "expected 12 bytes of tls-unique but got %u", + static_cast<unsigned>(tls_unique_len)); + return false; + } + + if (WriteAll(ssl.get(), tls_unique, tls_unique_len) < 0) { + return false; + } + } + + if (config->send_alert) { + if (DoSendFatalAlert(ssl.get(), SSL_AD_DECOMPRESSION_FAILURE) < 0) { + return false; + } + return true; + } + + if (config->write_different_record_sizes) { + if (config->is_dtls) { + fprintf(stderr, "write_different_record_sizes not supported for DTLS\n"); + return false; + } + // This mode writes a number of different record sizes in an attempt to + // trip up the CBC record splitting code. + static const size_t kBufLen = 32769; + std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]); + OPENSSL_memset(buf.get(), 0x42, kBufLen); + static const size_t kRecordSizes[] = { + 0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769}; + for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kRecordSizes); i++) { + const size_t len = kRecordSizes[i]; + if (len > kBufLen) { + fprintf(stderr, "Bad kRecordSizes value.\n"); + return false; + } + if (WriteAll(ssl.get(), buf.get(), len) < 0) { + return false; + } + } + } else { + if (config->read_with_unfinished_write) { + if (!config->async) { + fprintf(stderr, "-read-with-unfinished-write requires -async.\n"); + return false; + } + + int write_ret = SSL_write(ssl.get(), + reinterpret_cast<const uint8_t *>("unfinished"), 10); + if (SSL_get_error(ssl.get(), write_ret) != SSL_ERROR_WANT_WRITE) { + fprintf(stderr, "Failed to leave unfinished write.\n"); + return false; + } + } + if (config->shim_writes_first) { + if (WriteAll(ssl.get(), reinterpret_cast<const uint8_t *>("hello"), + 5) < 0) { + return false; + } + } + if (!config->shim_shuts_down) { + for (;;) { + // Read only 512 bytes at a time in TLS to ensure records may be + // returned in multiple reads. + size_t read_size = config->is_dtls ? 16384 : 512; + if (config->read_size > 0) { + read_size = config->read_size; + } + std::unique_ptr<uint8_t[]> buf(new uint8_t[read_size]); + + int n = DoRead(ssl.get(), buf.get(), read_size); + int err = SSL_get_error(ssl.get(), n); + if (err == SSL_ERROR_ZERO_RETURN || + (n == 0 && err == SSL_ERROR_SYSCALL)) { + if (n != 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return false; + } + // Stop on either clean or unclean shutdown. + break; + } else if (err != SSL_ERROR_NONE) { + if (n > 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return false; + } + return false; + } + // Successfully read data. + if (n <= 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return false; + } + + // After a successful read, with or without False Start, the handshake + // must be complete. + if (!GetTestState(ssl.get())->handshake_done) { + fprintf(stderr, "handshake was not completed after SSL_read\n"); + return false; + } + + for (int i = 0; i < n; i++) { + buf[i] ^= 0xff; + } + if (WriteAll(ssl.get(), buf.get(), n) < 0) { + return false; + } + } + } + } + + if (!config->is_server && !config->false_start && + !config->implicit_handshake && + // Session tickets are sent post-handshake in TLS 1.3. + GetProtocolVersion(ssl.get()) < TLS1_3_VERSION && + GetTestState(ssl.get())->got_new_session) { + fprintf(stderr, "new session was established after the handshake\n"); + return false; + } + + if (GetProtocolVersion(ssl.get()) >= TLS1_3_VERSION && !config->is_server) { + bool expect_new_session = + !config->expect_no_session && !config->shim_shuts_down; + if (expect_new_session != GetTestState(ssl.get())->got_new_session) { + fprintf(stderr, + "new session was%s cached, but we expected the opposite\n", + GetTestState(ssl.get())->got_new_session ? "" : " not"); + return false; + } + + if (expect_new_session) { + bool got_early_data_info = + GetTestState(ssl.get())->new_session->ticket_max_early_data != 0; + if (config->expect_early_data_info != got_early_data_info) { + fprintf( + stderr, + "new session did%s include ticket_early_data_info, but we expected " + "the opposite\n", + got_early_data_info ? "" : " not"); + return false; + } + } + } + + if (out_session) { + *out_session = std::move(GetTestState(ssl.get())->new_session); + } + + ret = DoShutdown(ssl.get()); + + if (config->shim_shuts_down && config->check_close_notify) { + // We initiate shutdown, so |SSL_shutdown| will return in two stages. First + // it returns zero when our close_notify is sent, then one when the peer's + // is received. + if (ret != 0) { + fprintf(stderr, "Unexpected SSL_shutdown result: %d != 0\n", ret); + return false; + } + ret = DoShutdown(ssl.get()); + } + + if (ret != 1) { + fprintf(stderr, "Unexpected SSL_shutdown result: %d != 1\n", ret); + return false; + } + + if (SSL_total_renegotiations(ssl.get()) != + config->expect_total_renegotiations) { + fprintf(stderr, "Expected %d renegotiations, got %d\n", + config->expect_total_renegotiations, + SSL_total_renegotiations(ssl.get())); + return false; + } + + return true; +} + +class StderrDelimiter { + public: + ~StderrDelimiter() { fprintf(stderr, "--- DONE ---\n"); } +}; + +int main(int argc, char **argv) { + // To distinguish ASan's output from ours, add a trailing message to stderr. + // Anything following this line will be considered an error. + StderrDelimiter delimiter; + +#if defined(OPENSSL_WINDOWS) + /* Initialize Winsock. */ + WORD wsa_version = MAKEWORD(2, 2); + WSADATA wsa_data; + int wsa_err = WSAStartup(wsa_version, &wsa_data); + if (wsa_err != 0) { + fprintf(stderr, "WSAStartup failed: %d\n", wsa_err); + return 1; + } + if (wsa_data.wVersion != wsa_version) { + fprintf(stderr, "Didn't get expected version: %x\n", wsa_data.wVersion); + return 1; + } +#else + signal(SIGPIPE, SIG_IGN); +#endif + + CRYPTO_library_init(); + g_config_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + g_state_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, TestStateExFree); + if (g_config_index < 0 || g_state_index < 0) { + return 1; + } + + TestConfig config; + if (!ParseConfig(argc - 1, argv + 1, &config)) { + return Usage(argv[0]); + } + + g_pool = CRYPTO_BUFFER_POOL_new(); + + // Some code treats the zero time special, so initialize the clock to a + // non-zero time. + g_clock.tv_sec = 1234; + g_clock.tv_usec = 1234; + + bssl::UniquePtr<SSL_CTX> ssl_ctx = SetupCtx(&config); + if (!ssl_ctx) { + ERR_print_errors_fp(stderr); + return 1; + } + + bssl::UniquePtr<SSL_SESSION> session; + for (int i = 0; i < config.resume_count + 1; i++) { + bool is_resume = i > 0; + if (is_resume && !config.is_server && !session) { + fprintf(stderr, "No session to offer.\n"); + return 1; + } + + bssl::UniquePtr<SSL_SESSION> offer_session = std::move(session); + if (!DoExchange(&session, ssl_ctx.get(), &config, is_resume, + offer_session.get())) { + fprintf(stderr, "Connection %d failed.\n", i + 1); + ERR_print_errors_fp(stderr); + return 1; + } + + if (config.resumption_delay != 0) { + g_clock.tv_sec += config.resumption_delay; + } + } + + return 0; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/packeted_bio.cc b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/packeted_bio.cc new file mode 100644 index 000000000..835dbfb1f --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/packeted_bio.cc @@ -0,0 +1,265 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "packeted_bio.h" + +#include <assert.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +#include <openssl/mem.h> + +#include "../../crypto/internal.h" + + +namespace { + +extern const BIO_METHOD g_packeted_bio_method; + +const uint8_t kOpcodePacket = 'P'; +const uint8_t kOpcodeTimeout = 'T'; +const uint8_t kOpcodeTimeoutAck = 't'; + +struct PacketedBio { + explicit PacketedBio(timeval *clock_arg) + : clock(clock_arg) { + OPENSSL_memset(&timeout, 0, sizeof(timeout)); + } + + bool HasTimeout() const { + return timeout.tv_sec != 0 || timeout.tv_usec != 0; + } + + timeval timeout; + timeval *clock; +}; + +PacketedBio *GetData(BIO *bio) { + if (bio->method != &g_packeted_bio_method) { + return NULL; + } + return (PacketedBio *)bio->ptr; +} + +// ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and +// 0 or -1 on error. +static int ReadAll(BIO *bio, uint8_t *out, size_t len) { + while (len > 0) { + int chunk_len = INT_MAX; + if (len <= INT_MAX) { + chunk_len = (int)len; + } + int ret = BIO_read(bio, out, chunk_len); + if (ret <= 0) { + return ret; + } + out += ret; + len -= ret; + } + return 1; +} + +static int PacketedWrite(BIO *bio, const char *in, int inl) { + if (bio->next_bio == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + // Write the header. + uint8_t header[5]; + header[0] = kOpcodePacket; + header[1] = (inl >> 24) & 0xff; + header[2] = (inl >> 16) & 0xff; + header[3] = (inl >> 8) & 0xff; + header[4] = inl & 0xff; + int ret = BIO_write(bio->next_bio, header, sizeof(header)); + if (ret <= 0) { + BIO_copy_next_retry(bio); + return ret; + } + + // Write the buffer. + ret = BIO_write(bio->next_bio, in, inl); + if (ret < 0 || (inl > 0 && ret == 0)) { + BIO_copy_next_retry(bio); + return ret; + } + assert(ret == inl); + return ret; +} + +static int PacketedRead(BIO *bio, char *out, int outl) { + PacketedBio *data = GetData(bio); + if (bio->next_bio == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + // Read the opcode. + uint8_t opcode; + int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode)); + if (ret <= 0) { + BIO_copy_next_retry(bio); + return ret; + } + + if (opcode == kOpcodeTimeout) { + // The caller is required to advance any pending timeouts before continuing. + if (data->HasTimeout()) { + fprintf(stderr, "Unprocessed timeout!\n"); + return -1; + } + + // Process the timeout. + uint8_t buf[8]; + ret = ReadAll(bio->next_bio, buf, sizeof(buf)); + if (ret <= 0) { + BIO_copy_next_retry(bio); + return ret; + } + uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) | + (static_cast<uint64_t>(buf[1]) << 48) | + (static_cast<uint64_t>(buf[2]) << 40) | + (static_cast<uint64_t>(buf[3]) << 32) | + (static_cast<uint64_t>(buf[4]) << 24) | + (static_cast<uint64_t>(buf[5]) << 16) | + (static_cast<uint64_t>(buf[6]) << 8) | + static_cast<uint64_t>(buf[7]); + timeout /= 1000; // Convert nanoseconds to microseconds. + + data->timeout.tv_usec = timeout % 1000000; + data->timeout.tv_sec = timeout / 1000000; + + // Send an ACK to the peer. + ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1); + if (ret <= 0) { + return ret; + } + assert(ret == 1); + + // Signal to the caller to retry the read, after advancing the clock. + BIO_set_retry_read(bio); + return -1; + } + + if (opcode != kOpcodePacket) { + fprintf(stderr, "Unknown opcode, %u\n", opcode); + return -1; + } + + // Read the length prefix. + uint8_t len_bytes[4]; + ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes)); + if (ret <= 0) { + BIO_copy_next_retry(bio); + return ret; + } + + uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) | + (len_bytes[2] << 8) | len_bytes[3]; + uint8_t *buf = (uint8_t *)OPENSSL_malloc(len); + if (buf == NULL) { + return -1; + } + ret = ReadAll(bio->next_bio, buf, len); + if (ret <= 0) { + fprintf(stderr, "Packeted BIO was truncated\n"); + return -1; + } + + if (outl > (int)len) { + outl = len; + } + OPENSSL_memcpy(out, buf, outl); + OPENSSL_free(buf); + return outl; +} + +static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) { + if (bio->next_bio == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + int ret = BIO_ctrl(bio->next_bio, cmd, num, ptr); + BIO_copy_next_retry(bio); + return ret; +} + +static int PacketedNew(BIO *bio) { + bio->init = 1; + return 1; +} + +static int PacketedFree(BIO *bio) { + if (bio == NULL) { + return 0; + } + + delete GetData(bio); + bio->init = 0; + return 1; +} + +static long PacketedCallbackCtrl(BIO *bio, int cmd, bio_info_cb fp) { + if (bio->next_bio == NULL) { + return 0; + } + return BIO_callback_ctrl(bio->next_bio, cmd, fp); +} + +const BIO_METHOD g_packeted_bio_method = { + BIO_TYPE_FILTER, + "packeted bio", + PacketedWrite, + PacketedRead, + NULL /* puts */, + NULL /* gets */, + PacketedCtrl, + PacketedNew, + PacketedFree, + PacketedCallbackCtrl, +}; + +} // namespace + +bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock) { + bssl::UniquePtr<BIO> bio(BIO_new(&g_packeted_bio_method)); + if (!bio) { + return nullptr; + } + bio->ptr = new PacketedBio(clock); + return bio; +} + +bool PacketedBioAdvanceClock(BIO *bio) { + PacketedBio *data = GetData(bio); + if (data == nullptr) { + return false; + } + + if (!data->HasTimeout()) { + return false; + } + + data->clock->tv_usec += data->timeout.tv_usec; + data->clock->tv_sec += data->clock->tv_usec / 1000000; + data->clock->tv_usec %= 1000000; + data->clock->tv_sec += data->timeout.tv_sec; + OPENSSL_memset(&data->timeout, 0, sizeof(data->timeout)); + return true; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/packeted_bio.h b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/packeted_bio.h new file mode 100644 index 000000000..7f07fcbf0 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/packeted_bio.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef HEADER_PACKETED_BIO +#define HEADER_PACKETED_BIO + +#include <openssl/base.h> +#include <openssl/bio.h> + +#if defined(OPENSSL_WINDOWS) +OPENSSL_MSVC_PRAGMA(warning(push, 3)) +#include <winsock2.h> +OPENSSL_MSVC_PRAGMA(warning(pop)) +#else +#include <sys/time.h> +#endif + + +// PacketedBioCreate creates a filter BIO which implements a reliable in-order +// blocking datagram socket. It uses the value of |*clock| as the clock. +// +// During a |BIO_read|, the peer may signal the filter BIO to simulate a +// timeout. The operation will fail immediately. The caller must then call +// |PacketedBioAdvanceClock| before retrying |BIO_read|. +bssl::UniquePtr<BIO> PacketedBioCreate(timeval *clock); + +// PacketedBioAdvanceClock advances |bio|'s clock and returns true if there is a +// pending timeout. Otherwise, it returns false. +bool PacketedBioAdvanceClock(BIO *bio); + + +#endif // HEADER_PACKETED_BIO diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/alert.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/alert.go new file mode 100644 index 000000000..652e9eec7 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/alert.go @@ -0,0 +1,93 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import "strconv" + +type alert uint8 + +const ( + // alert level + alertLevelWarning = 1 + alertLevelError = 2 +) + +const ( + alertCloseNotify alert = 0 + alertEndOfEarlyData alert = 1 + alertUnexpectedMessage alert = 10 + alertBadRecordMAC alert = 20 + alertDecryptionFailed alert = 21 + alertRecordOverflow alert = 22 + alertDecompressionFailure alert = 30 + alertHandshakeFailure alert = 40 + alertNoCertificate alert = 41 + alertBadCertificate alert = 42 + alertUnsupportedCertificate alert = 43 + alertCertificateRevoked alert = 44 + alertCertificateExpired alert = 45 + alertCertificateUnknown alert = 46 + alertIllegalParameter alert = 47 + alertUnknownCA alert = 48 + alertAccessDenied alert = 49 + alertDecodeError alert = 50 + alertDecryptError alert = 51 + alertProtocolVersion alert = 70 + alertInsufficientSecurity alert = 71 + alertInternalError alert = 80 + alertInappropriateFallback alert = 86 + alertUserCanceled alert = 90 + alertNoRenegotiation alert = 100 + alertMissingExtension alert = 109 + alertUnsupportedExtension alert = 110 + alertUnrecognizedName alert = 112 + alertUnknownPSKIdentity alert = 115 + alertCertificateRequired alert = 116 +) + +var alertText = map[alert]string{ + alertCloseNotify: "close notify", + alertEndOfEarlyData: "end of early data", + alertUnexpectedMessage: "unexpected message", + alertBadRecordMAC: "bad record MAC", + alertDecryptionFailed: "decryption failed", + alertRecordOverflow: "record overflow", + alertDecompressionFailure: "decompression failure", + alertHandshakeFailure: "handshake failure", + alertNoCertificate: "no certificate", + alertBadCertificate: "bad certificate", + alertUnsupportedCertificate: "unsupported certificate", + alertCertificateRevoked: "revoked certificate", + alertCertificateExpired: "expired certificate", + alertCertificateUnknown: "unknown certificate", + alertIllegalParameter: "illegal parameter", + alertUnknownCA: "unknown certificate authority", + alertAccessDenied: "access denied", + alertDecodeError: "error decoding message", + alertDecryptError: "error decrypting message", + alertProtocolVersion: "protocol version not supported", + alertInsufficientSecurity: "insufficient security level", + alertInternalError: "internal error", + alertInappropriateFallback: "inappropriate fallback", + alertUserCanceled: "user canceled", + alertNoRenegotiation: "no renegotiation", + alertMissingExtension: "missing extension", + alertUnsupportedExtension: "unsupported extension", + alertUnrecognizedName: "unrecognized name", + alertUnknownPSKIdentity: "unknown PSK identity", + alertCertificateRequired: "certificate required", +} + +func (e alert) String() string { + s, ok := alertText[e] + if ok { + return s + } + return "alert(" + strconv.Itoa(int(e)) + ")" +} + +func (e alert) Error() string { + return e.String() +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/cert.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/cert.pem new file mode 100644 index 000000000..c360dc731 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIJALW2IrlaBKUhMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTYwNzA5MDQzODA5WhcNMTYwODA4MDQzODA5WjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAugvahBkSAUF1fC49vb1bvlPrcl80kop1iLpiuYoz4Qptwy57+EWssZBc +HprZ5BkWf6PeGZ7F5AX1PyJbGHZLqvMCvViP6pd4MFox/igESISEHEixoiXCzepB +rhtp5UQSjHD4D4hKtgdMgVxX+LRtwgW3mnu/vBu7rzpr/DS8io99p3lqZ1Aky+aN +lcMj6MYy8U+YFEevb/V0lRY9oqwmW7BHnXikm/vi6sjIS350U8zb/mRzYeIs2R65 +LUduTL50+UMgat9ocewI2dv8aO9Dph+8NdGtg8LFYyTTHcUxJoMr1PTOgnmET19W +JH4PrFwk7ZE1QJQQ1L4iKmPeQistuQIDAQABo4GnMIGkMB0GA1UdDgQWBBT5m6Vv +zYjVYHG30iBE+j2XDhUE8jB1BgNVHSMEbjBsgBT5m6VvzYjVYHG30iBE+j2XDhUE +8qFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV +BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJALW2IrlaBKUhMAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAD7Jg68SArYWlcoHfZAB90Pmyrt5H6D8 +LRi+W2Ri1fBNxREELnezWJ2scjl4UMcsKYp4Pi950gVN+62IgrImcCNvtb5I1Cfy +/MNNur9ffas6X334D0hYVIQTePyFk3umI+2mJQrtZZyMPIKSY/sYGQHhGGX6wGK+ +GO/og0PQk/Vu6D+GU2XRnDV0YZg1lsAsHd21XryK6fDmNkEMwbIWrts4xc7scRrG +HWy+iMf6/7p/Ak/SIicM4XSwmlQ8pPxAZPr+E2LoVd9pMpWUwpW2UbtO5wsGTrY5 +sO45tFNN/y+jtUheB1C2ijObG/tXELaiyCdM+S/waeuv0MXtI4xnn1A= +-----END CERTIFICATE----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/chacha20_poly1305.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/chacha20_poly1305.go new file mode 100644 index 000000000..866f72421 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/chacha20_poly1305.go @@ -0,0 +1,194 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "crypto/cipher" + "crypto/subtle" + "encoding/binary" + "errors" + + "./poly1305" +) + +// See RFC 7539. + +func leftRotate(a uint32, n uint) uint32 { + return (a << n) | (a >> (32 - n)) +} + +func chaChaQuarterRound(state *[16]uint32, a, b, c, d int) { + state[a] += state[b] + state[d] = leftRotate(state[d]^state[a], 16) + + state[c] += state[d] + state[b] = leftRotate(state[b]^state[c], 12) + + state[a] += state[b] + state[d] = leftRotate(state[d]^state[a], 8) + + state[c] += state[d] + state[b] = leftRotate(state[b]^state[c], 7) +} + +func chaCha20Block(state *[16]uint32, out []byte) { + var workingState [16]uint32 + copy(workingState[:], state[:]) + for i := 0; i < 10; i++ { + chaChaQuarterRound(&workingState, 0, 4, 8, 12) + chaChaQuarterRound(&workingState, 1, 5, 9, 13) + chaChaQuarterRound(&workingState, 2, 6, 10, 14) + chaChaQuarterRound(&workingState, 3, 7, 11, 15) + chaChaQuarterRound(&workingState, 0, 5, 10, 15) + chaChaQuarterRound(&workingState, 1, 6, 11, 12) + chaChaQuarterRound(&workingState, 2, 7, 8, 13) + chaChaQuarterRound(&workingState, 3, 4, 9, 14) + } + for i := 0; i < 16; i++ { + binary.LittleEndian.PutUint32(out[i*4:i*4+4], workingState[i]+state[i]) + } +} + +// sliceForAppend takes a slice and a requested number of bytes. It returns a +// slice with the contents of the given slice followed by that many bytes and a +// second slice that aliases into it and contains only the extra bytes. If the +// original slice has sufficient capacity then no allocation is performed. +func sliceForAppend(in []byte, n int) (head, tail []byte) { + if total := len(in) + n; cap(in) >= total { + head = in[:total] + } else { + head = make([]byte, total) + copy(head, in) + } + tail = head[len(in):] + return +} + +func chaCha20(out, in, key, nonce []byte, counter uint64) { + var state [16]uint32 + state[0] = 0x61707865 + state[1] = 0x3320646e + state[2] = 0x79622d32 + state[3] = 0x6b206574 + for i := 0; i < 8; i++ { + state[4+i] = binary.LittleEndian.Uint32(key[i*4 : i*4+4]) + } + + switch len(nonce) { + case 8: + state[14] = binary.LittleEndian.Uint32(nonce[0:4]) + state[15] = binary.LittleEndian.Uint32(nonce[4:8]) + case 12: + state[13] = binary.LittleEndian.Uint32(nonce[0:4]) + state[14] = binary.LittleEndian.Uint32(nonce[4:8]) + state[15] = binary.LittleEndian.Uint32(nonce[8:12]) + default: + panic("bad nonce length") + } + + for i := 0; i < len(in); i += 64 { + state[12] = uint32(counter) + + var tmp [64]byte + chaCha20Block(&state, tmp[:]) + count := 64 + if len(in)-i < count { + count = len(in) - i + } + for j := 0; j < count; j++ { + out[i+j] = in[i+j] ^ tmp[j] + } + + counter++ + } +} + +// chaCha20Poly1305 implements the AEAD from +// RFC 7539 and draft-agl-tls-chacha20poly1305-04. +type chaCha20Poly1305 struct { + key [32]byte +} + +func newChaCha20Poly1305(key []byte) (cipher.AEAD, error) { + if len(key) != 32 { + return nil, errors.New("bad key length") + } + aead := new(chaCha20Poly1305) + copy(aead.key[:], key) + return aead, nil +} + +func (c *chaCha20Poly1305) NonceSize() int { + return 12 +} + +func (c *chaCha20Poly1305) Overhead() int { return 16 } + +func (c *chaCha20Poly1305) poly1305(tag *[16]byte, nonce, ciphertext, additionalData []byte) { + input := make([]byte, 0, len(additionalData)+15+len(ciphertext)+15+8+8) + input = append(input, additionalData...) + var zeros [15]byte + if pad := len(input) % 16; pad != 0 { + input = append(input, zeros[:16-pad]...) + } + input = append(input, ciphertext...) + if pad := len(input) % 16; pad != 0 { + input = append(input, zeros[:16-pad]...) + } + input, out := sliceForAppend(input, 8) + binary.LittleEndian.PutUint64(out, uint64(len(additionalData))) + input, out = sliceForAppend(input, 8) + binary.LittleEndian.PutUint64(out, uint64(len(ciphertext))) + + var poly1305Key [32]byte + chaCha20(poly1305Key[:], poly1305Key[:], c.key[:], nonce, 0) + + poly1305.Sum(tag, input, &poly1305Key) +} + +func (c *chaCha20Poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + if len(nonce) != c.NonceSize() { + panic("Bad nonce length") + } + + ret, out := sliceForAppend(dst, len(plaintext)+16) + chaCha20(out[:len(plaintext)], plaintext, c.key[:], nonce, 1) + + var tag [16]byte + c.poly1305(&tag, nonce, out[:len(plaintext)], additionalData) + copy(out[len(plaintext):], tag[:]) + + return ret +} + +func (c *chaCha20Poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if len(nonce) != c.NonceSize() { + panic("Bad nonce length") + } + if len(ciphertext) < 16 { + return nil, errors.New("chacha20: message authentication failed") + } + plaintextLen := len(ciphertext) - 16 + + var tag [16]byte + c.poly1305(&tag, nonce, ciphertext[:plaintextLen], additionalData) + if subtle.ConstantTimeCompare(tag[:], ciphertext[plaintextLen:]) != 1 { + return nil, errors.New("chacha20: message authentication failed") + } + + ret, out := sliceForAppend(dst, plaintextLen) + chaCha20(out, ciphertext[:plaintextLen], c.key[:], nonce, 1) + return ret, nil +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/chacha20_poly1305_test.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/chacha20_poly1305_test.go new file mode 100644 index 000000000..38c4b70fb --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/chacha20_poly1305_test.go @@ -0,0 +1,144 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "bytes" + "encoding/hex" + "testing" +) + +// See RFC 7539, section 2.1.1. +func TestChaChaQuarterRound(t *testing.T) { + state := [16]uint32{0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567} + chaChaQuarterRound(&state, 0, 1, 2, 3) + + a, b, c, d := state[0], state[1], state[2], state[3] + if a != 0xea2a92f4 || b != 0xcb1cf8ce || c != 0x4581472e || d != 0x5881c4bb { + t.Errorf("Incorrect results: %x", state) + } +} + +// See RFC 7539, section 2.2.1. +func TestChaChaQuarterRoundState(t *testing.T) { + state := [16]uint32{ + 0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a, + 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c, + 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963, + 0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320, + } + chaChaQuarterRound(&state, 2, 7, 8, 13) + + expected := [16]uint32{ + 0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a, + 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0xcfacafd2, + 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963, + 0x5c971061, 0xccc07c79, 0x2098d9d6, 0x91dbd320, + } + for i := range state { + if state[i] != expected[i] { + t.Errorf("Mismatch at %d: %x vs %x", i, state, expected) + } + } +} + +// See RFC 7539, section 2.3.2. +func TestChaCha20Block(t *testing.T) { + state := [16]uint32{ + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, + 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, + 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, + 0x00000001, 0x09000000, 0x4a000000, 0x00000000, + } + out := make([]byte, 64) + chaCha20Block(&state, out) + + expected := []byte{ + 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, + 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4, + 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, + 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e, + 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, + 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2, + 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, + 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e, + } + if !bytes.Equal(out, expected) { + t.Errorf("Got %x, wanted %x", out, expected) + } +} + +func decodeHexOrPanic(in string) []byte { + out, err := hex.DecodeString(in) + if err != nil { + panic(err) + } + return out +} + +var chaCha20Poly1305TestVectors = []struct { + key, input, nonce, ad, output string +}{ + { + // See RFC 7539, section 2.8.2. + key: "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f", + input: "4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e", + nonce: "070000004041424344454647", + ad: "50515253c0c1c2c3c4c5c6c7", + output: "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691", + }, + { + // See RFC 7539, section A.5. + key: "1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0", + input: "496e7465726e65742d4472616674732061726520647261667420646f63756d656e74732076616c696420666f722061206d6178696d756d206f6620736978206d6f6e74687320616e64206d617920626520757064617465642c207265706c616365642c206f72206f62736f6c65746564206279206f7468657220646f63756d656e747320617420616e792074696d652e20497420697320696e617070726f70726961746520746f2075736520496e7465726e65742d447261667473206173207265666572656e6365206d6174657269616c206f7220746f2063697465207468656d206f74686572207468616e206173202fe2809c776f726b20696e2070726f67726573732e2fe2809d", + nonce: "000000000102030405060708", + ad: "f33388860000000000004e91", + output: "64a0861575861af460f062c79be643bd5e805cfd345cf389f108670ac76c8cb24c6cfc18755d43eea09ee94e382d26b0bdb7b73c321b0100d4f03b7f355894cf332f830e710b97ce98c8a84abd0b948114ad176e008d33bd60f982b1ff37c8559797a06ef4f0ef61c186324e2b3506383606907b6a7c02b0f9f6157b53c867e4b9166c767b804d46a59b5216cde7a4e99040c5a40433225ee282a1b0a06c523eaf4534d7f83fa1155b0047718cbc546a0d072b04b3564eea1b422273f548271a0bb2316053fa76991955ebd63159434ecebb4e466dae5a1073a6727627097a1049e617d91d361094fa68f0ff77987130305beaba2eda04df997b714d6c6f2c29a6ad5cb4022b02709beead9d67890cbb22392336fea1851f38", + }, +} + +// See draft-agl-tls-chacha20poly1305-04, section 7. +func TestChaCha20Poly1305(t *testing.T) { + for i, tt := range chaCha20Poly1305TestVectors { + key := decodeHexOrPanic(tt.key) + input := decodeHexOrPanic(tt.input) + nonce := decodeHexOrPanic(tt.nonce) + ad := decodeHexOrPanic(tt.ad) + output := decodeHexOrPanic(tt.output) + + aead, err := newChaCha20Poly1305(key) + if err != nil { + t.Fatal(err) + } + + out, err := aead.Open(nil, nonce, output, ad) + if err != nil { + t.Errorf("%d. Open failed: %s", i, err) + } else if !bytes.Equal(out, input) { + t.Errorf("%d. Open gave %x, wanted %x", i, out, input) + } + + out = aead.Seal(nil, nonce, input, ad) + if !bytes.Equal(out, output) { + t.Errorf("%d. Open gave %x, wanted %x", i, out, output) + } + + out[0]++ + _, err = aead.Open(nil, nonce, out, ad) + if err == nil { + t.Errorf("%d. Open on malformed data unexpectedly succeeded", i) + } + } +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/channel_id_key.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/channel_id_key.pem new file mode 100644 index 000000000..604752bcd --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/channel_id_key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIPwxu50c7LEhVNRYJFRWBUnoaz7JSos96T5hBp4rjyptoAoGCCqGSM49 +AwEHoUQDQgAEzFSVTE5guxJRQ0VbZ8dicPs5e/DT7xpW7Yc9hq0VOchv7cbXuI/T +CwadDjGWX/oaz0ftFqrVmfkwZu+C58ioWg== +-----END EC PRIVATE KEY----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/cipher_suites.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/cipher_suites.go new file mode 100644 index 000000000..a2c6fbfcc --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/cipher_suites.go @@ -0,0 +1,515 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/des" + "crypto/hmac" + "crypto/md5" + "crypto/rc4" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "crypto/x509" + "hash" +) + +// a keyAgreement implements the client and server side of a TLS key agreement +// protocol by generating and processing key exchange messages. +type keyAgreement interface { + // On the server side, the first two methods are called in order. + + // In the case that the key agreement protocol doesn't use a + // ServerKeyExchange message, generateServerKeyExchange can return nil, + // nil. + generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error) + processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error) + + // On the client side, the next two methods are called in order. + + // This method may not be called if the server doesn't send a + // ServerKeyExchange message. + processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error + generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) + + // peerSignatureAlgorithm returns the signature algorithm used by the + // peer, or zero if not applicable. + peerSignatureAlgorithm() signatureAlgorithm +} + +const ( + // suiteECDH indicates that the cipher suite involves elliptic curve + // Diffie-Hellman. This means that it should only be selected when the + // client indicates that it supports ECC with a curve and point format + // that we're happy with. + suiteECDHE = 1 << iota + // suiteECDSA indicates that the cipher suite involves an ECDSA + // signature and therefore may only be selected when the server's + // certificate is ECDSA. If this is not set then the cipher suite is + // RSA based. + suiteECDSA + // suiteTLS12 indicates that the cipher suite should only be advertised + // and accepted when using TLS 1.2 or greater. + suiteTLS12 + // suiteTLS13 indicates that the cipher suite can be used with TLS 1.3. + // Cipher suites lacking this flag may not be used with TLS 1.3. + suiteTLS13 + // suiteSHA384 indicates that the cipher suite uses SHA384 as the + // handshake hash. + suiteSHA384 + // suiteNoDTLS indicates that the cipher suite cannot be used + // in DTLS. + suiteNoDTLS + // suitePSK indicates that the cipher suite authenticates with + // a pre-shared key rather than a server private key. + suitePSK +) + +type tlsAead struct { + cipher.AEAD + explicitNonce bool +} + +// A cipherSuite is a specific combination of key agreement, cipher and MAC +// function. All cipher suites currently assume RSA key agreement. +type cipherSuite struct { + id uint16 + // the lengths, in bytes, of the key material needed for each component. + keyLen int + macLen int + ivLen func(version uint16) int + ka func(version uint16) keyAgreement + // flags is a bitmask of the suite* values, above. + flags int + cipher func(key, iv []byte, isRead bool) interface{} + mac func(version uint16, macKey []byte) macFunction + aead func(version uint16, key, fixedNonce []byte) *tlsAead +} + +func (cs cipherSuite) hash() crypto.Hash { + if cs.flags&suiteSHA384 != 0 { + return crypto.SHA384 + } + return crypto.SHA256 +} + +var cipherSuites = []*cipherSuite{ + // Ciphersuite order is chosen so that ECDHE comes before plain RSA + // and RC4 comes before AES (because of the Lucky13 attack). + {TLS_CHACHA20_POLY1305_SHA256, 32, 0, ivLenChaCha20Poly1305, nil, suiteTLS13, nil, nil, aeadCHACHA20POLY1305}, + {TLS_AES_128_GCM_SHA256, 16, 0, ivLenAESGCM, nil, suiteTLS13, nil, nil, aeadAESGCM}, + {TLS_AES_256_GCM_SHA384, 32, 0, ivLenAESGCM, nil, suiteTLS13 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, ivLenChaCha20Poly1305, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305}, + {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, ivLenChaCha20Poly1305, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305}, + {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, ivLenAESGCM, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, ivLenAESGCM, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, ivLenAESGCM, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, ivLenAESGCM, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, noIV, ecdheRSAKA, suiteECDHE | suiteNoDTLS, cipherRC4, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, noIV, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteNoDTLS, cipherRC4, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, ivLenAES, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, ivLenAES, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, ivLenAES, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, ivLenAES, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 32, 48, ivLenAES, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, ivLenAES, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil}, + {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, ivLenAES, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, ivLenAES, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, + {TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, ivLenAESGCM, dheRSAKA, suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, ivLenAESGCM, dheRSAKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, ivLenAES, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, 32, 32, ivLenAES, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_DHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, ivLenAES, dheRSAKA, 0, cipherAES, macSHA1, nil}, + {TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, ivLenAES, dheRSAKA, 0, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, ivLenAESGCM, rsaKA, suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, ivLenAESGCM, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_RSA_WITH_RC4_128_SHA, 16, 20, noIV, rsaKA, suiteNoDTLS, cipherRC4, macSHA1, nil}, + {TLS_RSA_WITH_RC4_128_MD5, 16, 16, noIV, rsaKA, suiteNoDTLS, cipherRC4, macMD5, nil}, + {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, ivLenAES, rsaKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_RSA_WITH_AES_256_CBC_SHA256, 32, 32, ivLenAES, rsaKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, ivLenAES, rsaKA, 0, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, ivLenAES, rsaKA, 0, cipherAES, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, ivLen3DES, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil}, + {TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, ivLen3DES, dheRSAKA, 0, cipher3DES, macSHA1, nil}, + {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, ivLen3DES, rsaKA, 0, cipher3DES, macSHA1, nil}, + {TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, 32, 0, ivLenChaCha20Poly1305, ecdhePSKKA, suiteECDHE | suitePSK | suiteTLS12, nil, nil, aeadCHACHA20POLY1305}, + {TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 16, 20, ivLenAES, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil}, + {TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 32, 20, ivLenAES, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil}, + {TLS_PSK_WITH_RC4_128_SHA, 16, 20, noIV, pskKA, suiteNoDTLS | suitePSK, cipherRC4, macSHA1, nil}, + {TLS_PSK_WITH_AES_128_CBC_SHA, 16, 20, ivLenAES, pskKA, suitePSK, cipherAES, macSHA1, nil}, + {TLS_PSK_WITH_AES_256_CBC_SHA, 32, 20, ivLenAES, pskKA, suitePSK, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_NULL_SHA, 0, 20, noIV, rsaKA, suiteNoDTLS, cipherNull, macSHA1, nil}, +} + +func noIV(vers uint16) int { + return 0 +} + +func ivLenChaCha20Poly1305(vers uint16) int { + return 12 +} + +func ivLenAESGCM(vers uint16) int { + if vers >= VersionTLS13 { + return 12 + } + return 4 +} + +func ivLenAES(vers uint16) int { + return 16 +} + +func ivLen3DES(vers uint16) int { + return 8 +} + +type nullCipher struct{} + +func cipherNull(key, iv []byte, isRead bool) interface{} { + return nullCipher{} +} + +func cipherRC4(key, iv []byte, isRead bool) interface{} { + cipher, _ := rc4.NewCipher(key) + return cipher +} + +func cipher3DES(key, iv []byte, isRead bool) interface{} { + block, _ := des.NewTripleDESCipher(key) + if isRead { + return cipher.NewCBCDecrypter(block, iv) + } + return cipher.NewCBCEncrypter(block, iv) +} + +func cipherAES(key, iv []byte, isRead bool) interface{} { + block, _ := aes.NewCipher(key) + if isRead { + return cipher.NewCBCDecrypter(block, iv) + } + return cipher.NewCBCEncrypter(block, iv) +} + +// macSHA1 returns a macFunction for the given protocol version. +func macSHA1(version uint16, key []byte) macFunction { + if version == VersionSSL30 { + mac := ssl30MAC{ + h: sha1.New(), + key: make([]byte, len(key)), + } + copy(mac.key, key) + return mac + } + return tls10MAC{hmac.New(sha1.New, key)} +} + +func macMD5(version uint16, key []byte) macFunction { + if version == VersionSSL30 { + mac := ssl30MAC{ + h: md5.New(), + key: make([]byte, len(key)), + } + copy(mac.key, key) + return mac + } + return tls10MAC{hmac.New(md5.New, key)} +} + +func macSHA256(version uint16, key []byte) macFunction { + if version == VersionSSL30 { + mac := ssl30MAC{ + h: sha256.New(), + key: make([]byte, len(key)), + } + copy(mac.key, key) + return mac + } + return tls10MAC{hmac.New(sha256.New, key)} +} + +func macSHA384(version uint16, key []byte) macFunction { + if version == VersionSSL30 { + mac := ssl30MAC{ + h: sha512.New384(), + key: make([]byte, len(key)), + } + copy(mac.key, key) + return mac + } + return tls10MAC{hmac.New(sha512.New384, key)} +} + +type macFunction interface { + Size() int + MAC(digestBuf, seq, header, length, data []byte) []byte +} + +// fixedNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to +// each call. +type fixedNonceAEAD struct { + // sealNonce and openNonce are buffers where the larger nonce will be + // constructed. Since a seal and open operation may be running + // concurrently, there is a separate buffer for each. + sealNonce, openNonce []byte + aead cipher.AEAD +} + +func (f *fixedNonceAEAD) NonceSize() int { return 8 } +func (f *fixedNonceAEAD) Overhead() int { return f.aead.Overhead() } + +func (f *fixedNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { + copy(f.sealNonce[len(f.sealNonce)-8:], nonce) + return f.aead.Seal(out, f.sealNonce, plaintext, additionalData) +} + +func (f *fixedNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) { + copy(f.openNonce[len(f.openNonce)-8:], nonce) + return f.aead.Open(out, f.openNonce, plaintext, additionalData) +} + +func aeadAESGCM(version uint16, key, fixedNonce []byte) *tlsAead { + aes, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + aead, err := cipher.NewGCM(aes) + if err != nil { + panic(err) + } + + nonce1, nonce2 := make([]byte, 12), make([]byte, 12) + copy(nonce1, fixedNonce) + copy(nonce2, fixedNonce) + + if version >= VersionTLS13 { + return &tlsAead{&xorNonceAEAD{nonce1, nonce2, aead}, false} + } + + return &tlsAead{&fixedNonceAEAD{nonce1, nonce2, aead}, true} +} + +func xorSlice(out, in []byte) { + for i := range out { + out[i] ^= in[i] + } +} + +// xorNonceAEAD wraps an AEAD and XORs a fixed portion of the nonce, left-padded +// if necessary, each call. +type xorNonceAEAD struct { + // sealNonce and openNonce are buffers where the larger nonce will be + // constructed. Since a seal and open operation may be running + // concurrently, there is a separate buffer for each. + sealNonce, openNonce []byte + aead cipher.AEAD +} + +func (x *xorNonceAEAD) NonceSize() int { return 8 } +func (x *xorNonceAEAD) Overhead() int { return x.aead.Overhead() } + +func (x *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { + xorSlice(x.sealNonce[len(x.sealNonce)-len(nonce):], nonce) + ret := x.aead.Seal(out, x.sealNonce, plaintext, additionalData) + xorSlice(x.sealNonce[len(x.sealNonce)-len(nonce):], nonce) + return ret +} + +func (x *xorNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) { + xorSlice(x.openNonce[len(x.openNonce)-len(nonce):], nonce) + ret, err := x.aead.Open(out, x.openNonce, plaintext, additionalData) + xorSlice(x.openNonce[len(x.openNonce)-len(nonce):], nonce) + return ret, err +} + +func aeadCHACHA20POLY1305(version uint16, key, fixedNonce []byte) *tlsAead { + aead, err := newChaCha20Poly1305(key) + if err != nil { + panic(err) + } + + nonce1, nonce2 := make([]byte, len(fixedNonce)), make([]byte, len(fixedNonce)) + copy(nonce1, fixedNonce) + copy(nonce2, fixedNonce) + + return &tlsAead{&xorNonceAEAD{nonce1, nonce2, aead}, false} +} + +// ssl30MAC implements the SSLv3 MAC function, as defined in +// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1 +type ssl30MAC struct { + h hash.Hash + key []byte +} + +func (s ssl30MAC) Size() int { + return s.h.Size() +} + +var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36} + +var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c} + +func (s ssl30MAC) MAC(digestBuf, seq, header, length, data []byte) []byte { + padLength := 48 + if s.h.Size() == 20 { + padLength = 40 + } + + s.h.Reset() + s.h.Write(s.key) + s.h.Write(ssl30Pad1[:padLength]) + s.h.Write(seq) + s.h.Write(header[:1]) + s.h.Write(length) + s.h.Write(data) + digestBuf = s.h.Sum(digestBuf[:0]) + + s.h.Reset() + s.h.Write(s.key) + s.h.Write(ssl30Pad2[:padLength]) + s.h.Write(digestBuf) + return s.h.Sum(digestBuf[:0]) +} + +// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3. +type tls10MAC struct { + h hash.Hash +} + +func (s tls10MAC) Size() int { + return s.h.Size() +} + +func (s tls10MAC) MAC(digestBuf, seq, header, length, data []byte) []byte { + s.h.Reset() + s.h.Write(seq) + s.h.Write(header) + s.h.Write(length) + s.h.Write(data) + return s.h.Sum(digestBuf[:0]) +} + +func rsaKA(version uint16) keyAgreement { + return &rsaKeyAgreement{version: version} +} + +func ecdheECDSAKA(version uint16) keyAgreement { + return &ecdheKeyAgreement{ + auth: &signedKeyAgreement{ + keyType: keyTypeECDSA, + version: version, + }, + } +} + +func ecdheRSAKA(version uint16) keyAgreement { + return &ecdheKeyAgreement{ + auth: &signedKeyAgreement{ + keyType: keyTypeRSA, + version: version, + }, + } +} + +func dheRSAKA(version uint16) keyAgreement { + return &dheKeyAgreement{ + auth: &signedKeyAgreement{ + keyType: keyTypeRSA, + version: version, + }, + } +} + +func pskKA(version uint16) keyAgreement { + return &pskKeyAgreement{ + base: &nilKeyAgreement{}, + } +} + +func ecdhePSKKA(version uint16) keyAgreement { + return &pskKeyAgreement{ + base: &ecdheKeyAgreement{ + auth: &nilKeyAgreementAuthentication{}, + }, + } +} + +// mutualCipherSuite returns a cipherSuite given a list of supported +// ciphersuites and the id requested by the peer. +func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { + for _, id := range have { + if id == want { + return cipherSuiteFromID(id) + } + } + return nil +} + +func cipherSuiteFromID(id uint16) *cipherSuite { + for _, suite := range cipherSuites { + if suite.id == id { + return suite + } + } + return nil +} + +// A list of the possible cipher suite ids. Taken from +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml +const ( + TLS_RSA_WITH_NULL_SHA uint16 = 0x0002 + TLS_RSA_WITH_RC4_128_MD5 uint16 = 0x0004 + TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 + TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x0016 + TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f + TLS_DHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0x0033 + TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 + TLS_DHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0039 + TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c + TLS_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x003d + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x0067 + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 uint16 = 0x006b + TLS_PSK_WITH_RC4_128_SHA uint16 = 0x008a + TLS_PSK_WITH_AES_128_CBC_SHA uint16 = 0x008c + TLS_PSK_WITH_AES_256_CBC_SHA uint16 = 0x008d + TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c + TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009e + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009f + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009 + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a + TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023 + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 uint16 = 0xc024 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 uint16 = 0xc028 + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030 + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xc035 + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xc036 + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8 + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9 + TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xccac + renegotiationSCSV uint16 = 0x00ff + fallbackSCSV uint16 = 0x5600 +) + +// Additional cipher suite IDs, not IANA-assigned. +const ( + TLS_AES_128_GCM_SHA256 uint16 = 0x1301 + TLS_AES_256_GCM_SHA384 uint16 = 0x1302 + TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303 +) diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/common.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/common.go new file mode 100644 index 000000000..167e872b0 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/common.go @@ -0,0 +1,1681 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "container/list" + "crypto" + "crypto/ecdsa" + "crypto/rand" + "crypto/x509" + "fmt" + "io" + "math/big" + "strings" + "sync" + "time" +) + +const ( + VersionSSL30 = 0x0300 + VersionTLS10 = 0x0301 + VersionTLS11 = 0x0302 + VersionTLS12 = 0x0303 + VersionTLS13 = 0x0304 +) + +// A draft version of TLS 1.3 that is sent over the wire for the current draft. +const tls13DraftVersion = 0x7f12 + +const ( + maxPlaintext = 16384 // maximum plaintext payload length + maxCiphertext = 16384 + 2048 // maximum ciphertext payload length + tlsRecordHeaderLen = 5 // record header length + dtlsRecordHeaderLen = 13 + maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) + + minVersion = VersionSSL30 + maxVersion = VersionTLS13 +) + +// TLS record types. +type recordType uint8 + +const ( + recordTypeChangeCipherSpec recordType = 20 + recordTypeAlert recordType = 21 + recordTypeHandshake recordType = 22 + recordTypeApplicationData recordType = 23 +) + +// TLS handshake message types. +const ( + typeHelloRequest uint8 = 0 + typeClientHello uint8 = 1 + typeServerHello uint8 = 2 + typeHelloVerifyRequest uint8 = 3 + typeNewSessionTicket uint8 = 4 + typeHelloRetryRequest uint8 = 6 // draft-ietf-tls-tls13-16 + typeEncryptedExtensions uint8 = 8 // draft-ietf-tls-tls13-16 + typeCertificate uint8 = 11 + typeServerKeyExchange uint8 = 12 + typeCertificateRequest uint8 = 13 + typeServerHelloDone uint8 = 14 + typeCertificateVerify uint8 = 15 + typeClientKeyExchange uint8 = 16 + typeFinished uint8 = 20 + typeCertificateStatus uint8 = 22 + typeKeyUpdate uint8 = 24 // draft-ietf-tls-tls13-16 + typeNextProtocol uint8 = 67 // Not IANA assigned + typeChannelID uint8 = 203 // Not IANA assigned +) + +// TLS compression types. +const ( + compressionNone uint8 = 0 +) + +// TLS extension numbers +const ( + extensionServerName uint16 = 0 + extensionStatusRequest uint16 = 5 + extensionSupportedCurves uint16 = 10 + extensionSupportedPoints uint16 = 11 + extensionSignatureAlgorithms uint16 = 13 + extensionUseSRTP uint16 = 14 + extensionALPN uint16 = 16 + extensionSignedCertificateTimestamp uint16 = 18 + extensionExtendedMasterSecret uint16 = 23 + extensionSessionTicket uint16 = 35 + extensionKeyShare uint16 = 40 // draft-ietf-tls-tls13-16 + extensionPreSharedKey uint16 = 41 // draft-ietf-tls-tls13-16 + extensionEarlyData uint16 = 42 // draft-ietf-tls-tls13-16 + extensionSupportedVersions uint16 = 43 // draft-ietf-tls-tls13-16 + extensionCookie uint16 = 44 // draft-ietf-tls-tls13-16 + extensionPSKKeyExchangeModes uint16 = 45 // draft-ietf-tls-tls13-18 + extensionTicketEarlyDataInfo uint16 = 46 // draft-ietf-tls-tls13-18 + extensionCustom uint16 = 1234 // not IANA assigned + extensionNextProtoNeg uint16 = 13172 // not IANA assigned + extensionRenegotiationInfo uint16 = 0xff01 + extensionChannelID uint16 = 30032 // not IANA assigned +) + +// TLS signaling cipher suite values +const ( + scsvRenegotiation uint16 = 0x00ff +) + +// CurveID is the type of a TLS identifier for an elliptic curve. See +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8 +type CurveID uint16 + +const ( + CurveP224 CurveID = 21 + CurveP256 CurveID = 23 + CurveP384 CurveID = 24 + CurveP521 CurveID = 25 + CurveX25519 CurveID = 29 +) + +// TLS Elliptic Curve Point Formats +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 +const ( + pointFormatUncompressed uint8 = 0 + pointFormatCompressedPrime uint8 = 1 +) + +// TLS CertificateStatusType (RFC 3546) +const ( + statusTypeOCSP uint8 = 1 +) + +// Certificate types (for certificateRequestMsg) +const ( + CertTypeRSASign = 1 // A certificate containing an RSA key + CertTypeDSSSign = 2 // A certificate containing a DSA key + CertTypeRSAFixedDH = 3 // A certificate containing a static DH key + CertTypeDSSFixedDH = 4 // A certificate containing a static DH key + + // See RFC4492 sections 3 and 5.5. + CertTypeECDSASign = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA. + CertTypeRSAFixedECDH = 65 // A certificate containing an ECDH-capable public key, signed with RSA. + CertTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA. + + // Rest of these are reserved by the TLS spec +) + +// signatureAlgorithm corresponds to a SignatureScheme value from TLS 1.3. Note +// that TLS 1.3 names the production 'SignatureScheme' to avoid colliding with +// TLS 1.2's SignatureAlgorithm but otherwise refers to them as 'signature +// algorithms' throughout. We match the latter. +type signatureAlgorithm uint16 + +const ( + // RSASSA-PKCS1-v1_5 algorithms + signatureRSAPKCS1WithMD5 signatureAlgorithm = 0x0101 + signatureRSAPKCS1WithSHA1 signatureAlgorithm = 0x0201 + signatureRSAPKCS1WithSHA256 signatureAlgorithm = 0x0401 + signatureRSAPKCS1WithSHA384 signatureAlgorithm = 0x0501 + signatureRSAPKCS1WithSHA512 signatureAlgorithm = 0x0601 + + // ECDSA algorithms + signatureECDSAWithSHA1 signatureAlgorithm = 0x0203 + signatureECDSAWithP256AndSHA256 signatureAlgorithm = 0x0403 + signatureECDSAWithP384AndSHA384 signatureAlgorithm = 0x0503 + signatureECDSAWithP521AndSHA512 signatureAlgorithm = 0x0603 + + // RSASSA-PSS algorithms + signatureRSAPSSWithSHA256 signatureAlgorithm = 0x0804 + signatureRSAPSSWithSHA384 signatureAlgorithm = 0x0805 + signatureRSAPSSWithSHA512 signatureAlgorithm = 0x0806 + + // EdDSA algorithms + signatureEd25519 signatureAlgorithm = 0x0807 + signatureEd448 signatureAlgorithm = 0x0808 +) + +// supportedSignatureAlgorithms contains the default supported signature +// algorithms. +var supportedSignatureAlgorithms = []signatureAlgorithm{ + signatureRSAPSSWithSHA256, + signatureRSAPKCS1WithSHA256, + signatureECDSAWithP256AndSHA256, + signatureRSAPKCS1WithSHA1, + signatureECDSAWithSHA1, +} + +// SRTP protection profiles (See RFC 5764, section 4.1.2) +const ( + SRTP_AES128_CM_HMAC_SHA1_80 uint16 = 0x0001 + SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002 +) + +// PskKeyExchangeMode values (see draft-ietf-tls-tls13-16) +const ( + pskKEMode = 0 + pskDHEKEMode = 1 +) + +// KeyUpdateRequest values (see draft-ietf-tls-tls13-16, section 4.5.3) +const ( + keyUpdateNotRequested = 0 + keyUpdateRequested = 1 +) + +// ConnectionState records basic TLS details about the connection. +type ConnectionState struct { + Version uint16 // TLS version used by the connection (e.g. VersionTLS12) + HandshakeComplete bool // TLS handshake is complete + DidResume bool // connection resumes a previous TLS connection + CipherSuite uint16 // cipher suite in use (TLS_RSA_WITH_RC4_128_SHA, ...) + NegotiatedProtocol string // negotiated next protocol (from Config.NextProtos) + NegotiatedProtocolIsMutual bool // negotiated protocol was advertised by server + NegotiatedProtocolFromALPN bool // protocol negotiated with ALPN + ServerName string // server name requested by client, if any (server side only) + PeerCertificates []*x509.Certificate // certificate chain presented by remote peer + VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates + ChannelID *ecdsa.PublicKey // the channel ID for this connection + SRTPProtectionProfile uint16 // the negotiated DTLS-SRTP protection profile + TLSUnique []byte // the tls-unique channel binding + SCTList []byte // signed certificate timestamp list + PeerSignatureAlgorithm signatureAlgorithm // algorithm used by the peer in the handshake + CurveID CurveID // the curve used in ECDHE +} + +// ClientAuthType declares the policy the server will follow for +// TLS Client Authentication. +type ClientAuthType int + +const ( + NoClientCert ClientAuthType = iota + RequestClientCert + RequireAnyClientCert + VerifyClientCertIfGiven + RequireAndVerifyClientCert +) + +// ClientSessionState contains the state needed by clients to resume TLS +// sessions. +type ClientSessionState struct { + sessionId []uint8 // Session ID supplied by the server. nil if the session has a ticket. + sessionTicket []uint8 // Encrypted ticket used for session resumption with server + vers uint16 // SSL/TLS version negotiated for the session + cipherSuite uint16 // Ciphersuite negotiated for the session + masterSecret []byte // MasterSecret generated by client on a full handshake + handshakeHash []byte // Handshake hash for Channel ID purposes. + serverCertificates []*x509.Certificate // Certificate chain presented by the server + extendedMasterSecret bool // Whether an extended master secret was used to generate the session + sctList []byte + ocspResponse []byte + ticketCreationTime time.Time + ticketExpiration time.Time + ticketAgeAdd uint32 + maxEarlyDataSize uint32 +} + +// ClientSessionCache is a cache of ClientSessionState objects that can be used +// by a client to resume a TLS session with a given server. ClientSessionCache +// implementations should expect to be called concurrently from different +// goroutines. +type ClientSessionCache interface { + // Get searches for a ClientSessionState associated with the given key. + // On return, ok is true if one was found. + Get(sessionKey string) (session *ClientSessionState, ok bool) + + // Put adds the ClientSessionState to the cache with the given key. + Put(sessionKey string, cs *ClientSessionState) +} + +// ServerSessionCache is a cache of sessionState objects that can be used by a +// client to resume a TLS session with a given server. ServerSessionCache +// implementations should expect to be called concurrently from different +// goroutines. +type ServerSessionCache interface { + // Get searches for a sessionState associated with the given session + // ID. On return, ok is true if one was found. + Get(sessionId string) (session *sessionState, ok bool) + + // Put adds the sessionState to the cache with the given session ID. + Put(sessionId string, session *sessionState) +} + +// A Config structure is used to configure a TLS client or server. +// After one has been passed to a TLS function it must not be +// modified. A Config may be reused; the tls package will also not +// modify it. +type Config struct { + // Rand provides the source of entropy for nonces and RSA blinding. + // If Rand is nil, TLS uses the cryptographic random reader in package + // crypto/rand. + // The Reader must be safe for use by multiple goroutines. + Rand io.Reader + + // Time returns the current time as the number of seconds since the epoch. + // If Time is nil, TLS uses time.Now. + Time func() time.Time + + // Certificates contains one or more certificate chains + // to present to the other side of the connection. + // Server configurations must include at least one certificate. + Certificates []Certificate + + // NameToCertificate maps from a certificate name to an element of + // Certificates. Note that a certificate name can be of the form + // '*.example.com' and so doesn't have to be a domain name as such. + // See Config.BuildNameToCertificate + // The nil value causes the first element of Certificates to be used + // for all connections. + NameToCertificate map[string]*Certificate + + // RootCAs defines the set of root certificate authorities + // that clients use when verifying server certificates. + // If RootCAs is nil, TLS uses the host's root CA set. + RootCAs *x509.CertPool + + // NextProtos is a list of supported, application level protocols. + NextProtos []string + + // ServerName is used to verify the hostname on the returned + // certificates unless InsecureSkipVerify is given. It is also included + // in the client's handshake to support virtual hosting. + ServerName string + + // ClientAuth determines the server's policy for + // TLS Client Authentication. The default is NoClientCert. + ClientAuth ClientAuthType + + // ClientCAs defines the set of root certificate authorities + // that servers use if required to verify a client certificate + // by the policy in ClientAuth. + ClientCAs *x509.CertPool + + // ClientCertificateTypes defines the set of allowed client certificate + // types. The default is CertTypeRSASign and CertTypeECDSASign. + ClientCertificateTypes []byte + + // InsecureSkipVerify controls whether a client verifies the + // server's certificate chain and host name. + // If InsecureSkipVerify is true, TLS accepts any certificate + // presented by the server and any host name in that certificate. + // In this mode, TLS is susceptible to man-in-the-middle attacks. + // This should be used only for testing. + InsecureSkipVerify bool + + // CipherSuites is a list of supported cipher suites. If CipherSuites + // is nil, TLS uses a list of suites supported by the implementation. + CipherSuites []uint16 + + // PreferServerCipherSuites controls whether the server selects the + // client's most preferred ciphersuite, or the server's most preferred + // ciphersuite. If true then the server's preference, as expressed in + // the order of elements in CipherSuites, is used. + PreferServerCipherSuites bool + + // SessionTicketsDisabled may be set to true to disable session ticket + // (resumption) support. + SessionTicketsDisabled bool + + // SessionTicketKey is used by TLS servers to provide session + // resumption. See RFC 5077. If zero, it will be filled with + // random data before the first server handshake. + // + // If multiple servers are terminating connections for the same host + // they should all have the same SessionTicketKey. If the + // SessionTicketKey leaks, previously recorded and future TLS + // connections using that key are compromised. + SessionTicketKey [32]byte + + // ClientSessionCache is a cache of ClientSessionState entries + // for TLS session resumption. + ClientSessionCache ClientSessionCache + + // ServerSessionCache is a cache of sessionState entries for TLS session + // resumption. + ServerSessionCache ServerSessionCache + + // MinVersion contains the minimum SSL/TLS version that is acceptable. + // If zero, then SSLv3 is taken as the minimum. + MinVersion uint16 + + // MaxVersion contains the maximum SSL/TLS version that is acceptable. + // If zero, then the maximum version supported by this package is used, + // which is currently TLS 1.2. + MaxVersion uint16 + + // CurvePreferences contains the elliptic curves that will be used in + // an ECDHE handshake, in preference order. If empty, the default will + // be used. + CurvePreferences []CurveID + + // DefaultCurves contains the elliptic curves for which public values will + // be sent in the ClientHello's KeyShare extension. If this value is nil, + // all supported curves will have public values sent. This field is ignored + // on servers. + DefaultCurves []CurveID + + // ChannelID contains the ECDSA key for the client to use as + // its TLS Channel ID. + ChannelID *ecdsa.PrivateKey + + // RequestChannelID controls whether the server requests a TLS + // Channel ID. If negotiated, the client's public key is + // returned in the ConnectionState. + RequestChannelID bool + + // PreSharedKey, if not nil, is the pre-shared key to use with + // the PSK cipher suites. + PreSharedKey []byte + + // PreSharedKeyIdentity, if not empty, is the identity to use + // with the PSK cipher suites. + PreSharedKeyIdentity string + + // MaxEarlyDataSize controls the maximum number of bytes that the + // server will accept in early data and advertise in a + // NewSessionTicketMsg. If 0, no early data will be accepted and + // the TicketEarlyDataInfo extension in the NewSessionTicketMsg + // will be omitted. + MaxEarlyDataSize uint32 + + // SRTPProtectionProfiles, if not nil, is the list of SRTP + // protection profiles to offer in DTLS-SRTP. + SRTPProtectionProfiles []uint16 + + // SignSignatureAlgorithms, if not nil, overrides the default set of + // supported signature algorithms to sign with. + SignSignatureAlgorithms []signatureAlgorithm + + // VerifySignatureAlgorithms, if not nil, overrides the default set of + // supported signature algorithms that are accepted. + VerifySignatureAlgorithms []signatureAlgorithm + + // Bugs specifies optional misbehaviour to be used for testing other + // implementations. + Bugs ProtocolBugs + + serverInitOnce sync.Once // guards calling (*Config).serverInit +} + +type BadValue int + +const ( + BadValueNone BadValue = iota + BadValueNegative + BadValueZero + BadValueLimit + BadValueLarge + NumBadValues +) + +type RSABadValue int + +const ( + RSABadValueNone RSABadValue = iota + RSABadValueCorrupt + RSABadValueTooLong + RSABadValueTooShort + RSABadValueWrongVersion + NumRSABadValues +) + +type ProtocolBugs struct { + // InvalidSignature specifies that the signature in a ServerKeyExchange + // or CertificateVerify message should be invalid. + InvalidSignature bool + + // SendCurve, if non-zero, causes the server to send the specified curve + // ID in ServerKeyExchange (TLS 1.2) or ServerHello (TLS 1.3) rather + // than the negotiated one. + SendCurve CurveID + + // InvalidECDHPoint, if true, causes the ECC points in + // ServerKeyExchange or ClientKeyExchange messages to be invalid. + InvalidECDHPoint bool + + // BadECDSAR controls ways in which the 'r' value of an ECDSA signature + // can be invalid. + BadECDSAR BadValue + BadECDSAS BadValue + + // MaxPadding causes CBC records to have the maximum possible padding. + MaxPadding bool + // PaddingFirstByteBad causes the first byte of the padding to be + // incorrect. + PaddingFirstByteBad bool + // PaddingFirstByteBadIf255 causes the first byte of padding to be + // incorrect if there's a maximum amount of padding (i.e. 255 bytes). + PaddingFirstByteBadIf255 bool + + // FailIfNotFallbackSCSV causes a server handshake to fail if the + // client doesn't send the fallback SCSV value. + FailIfNotFallbackSCSV bool + + // DuplicateExtension causes an extra empty extension of bogus type to + // be emitted in either the ClientHello or the ServerHello. + DuplicateExtension bool + + // UnauthenticatedECDH causes the server to pretend ECDHE_RSA + // and ECDHE_ECDSA cipher suites are actually ECDH_anon. No + // Certificate message is sent and no signature is added to + // ServerKeyExchange. + UnauthenticatedECDH bool + + // SkipHelloVerifyRequest causes a DTLS server to skip the + // HelloVerifyRequest message. + SkipHelloVerifyRequest bool + + // SkipCertificateStatus, if true, causes the server to skip the + // CertificateStatus message. This is legal because CertificateStatus is + // optional, even with a status_request in ServerHello. + SkipCertificateStatus bool + + // SkipServerKeyExchange causes the server to skip sending + // ServerKeyExchange messages. + SkipServerKeyExchange bool + + // SkipNewSessionTicket causes the server to skip sending the + // NewSessionTicket message despite promising to in ServerHello. + SkipNewSessionTicket bool + + // SkipClientCertificate causes the client to skip the Certificate + // message. + SkipClientCertificate bool + + // SkipChangeCipherSpec causes the implementation to skip + // sending the ChangeCipherSpec message (and adjusting cipher + // state accordingly for the Finished message). + SkipChangeCipherSpec bool + + // SkipFinished causes the implementation to skip sending the Finished + // message. + SkipFinished bool + + // EarlyChangeCipherSpec causes the client to send an early + // ChangeCipherSpec message before the ClientKeyExchange. A value of + // zero disables this behavior. One and two configure variants for 0.9.8 + // and 1.0.1 modes, respectively. + EarlyChangeCipherSpec int + + // StrayChangeCipherSpec causes every pre-ChangeCipherSpec handshake + // message in DTLS to be prefaced by stray ChangeCipherSpec record. This + // may be used to test DTLS's handling of reordered ChangeCipherSpec. + StrayChangeCipherSpec bool + + // FragmentAcrossChangeCipherSpec causes the implementation to fragment + // the Finished (or NextProto) message around the ChangeCipherSpec + // messages. + FragmentAcrossChangeCipherSpec bool + + // SendUnencryptedFinished, if true, causes the Finished message to be + // send unencrypted before ChangeCipherSpec rather than after it. + SendUnencryptedFinished bool + + // PartialEncryptedExtensionsWithServerHello, if true, causes the TLS + // 1.3 server to send part of EncryptedExtensions unencrypted + // in the same record as ServerHello. + PartialEncryptedExtensionsWithServerHello bool + + // PartialClientFinishedWithClientHello, if true, causes the TLS 1.3 + // client to send part of Finished unencrypted in the same record as + // ClientHello. + PartialClientFinishedWithClientHello bool + + // SendV2ClientHello causes the client to send a V2ClientHello + // instead of a normal ClientHello. + SendV2ClientHello bool + + // SendFallbackSCSV causes the client to include + // TLS_FALLBACK_SCSV in the ClientHello. + SendFallbackSCSV bool + + // SendRenegotiationSCSV causes the client to include the renegotiation + // SCSV in the ClientHello. + SendRenegotiationSCSV bool + + // MaxHandshakeRecordLength, if non-zero, is the maximum size of a + // handshake record. Handshake messages will be split into multiple + // records at the specified size, except that the client_version will + // never be fragmented. For DTLS, it is the maximum handshake fragment + // size, not record size; DTLS allows multiple handshake fragments in a + // single handshake record. See |PackHandshakeFragments|. + MaxHandshakeRecordLength int + + // FragmentClientVersion will allow MaxHandshakeRecordLength to apply to + // the first 6 bytes of the ClientHello. + FragmentClientVersion bool + + // FragmentAlert will cause all alerts to be fragmented across + // two records. + FragmentAlert bool + + // DoubleAlert will cause all alerts to be sent as two copies packed + // within one record. + DoubleAlert bool + + // SendSpuriousAlert, if non-zero, will cause an spurious, unwanted + // alert to be sent. + SendSpuriousAlert alert + + // BadRSAClientKeyExchange causes the client to send a corrupted RSA + // ClientKeyExchange which would not pass padding checks. + BadRSAClientKeyExchange RSABadValue + + // RenewTicketOnResume causes the server to renew the session ticket and + // send a NewSessionTicket message during an abbreviated handshake. + RenewTicketOnResume bool + + // SendClientVersion, if non-zero, causes the client to send the + // specified value in the ClientHello version field. + SendClientVersion uint16 + + // OmitSupportedVersions, if true, causes the client to omit the + // supported versions extension. + OmitSupportedVersions bool + + // SendSupportedVersions, if non-empty, causes the client to send a + // supported versions extension with the values from array. + SendSupportedVersions []uint16 + + // NegotiateVersion, if non-zero, causes the server to negotiate the + // specifed TLS version rather than the version supported by either + // peer. + NegotiateVersion uint16 + + // NegotiateVersionOnRenego, if non-zero, causes the server to negotiate + // the specified TLS version on renegotiation rather than retaining it. + NegotiateVersionOnRenego uint16 + + // ExpectFalseStart causes the server to, on full handshakes, + // expect the peer to False Start; the server Finished message + // isn't sent until we receive an application data record + // from the peer. + ExpectFalseStart bool + + // AlertBeforeFalseStartTest, if non-zero, causes the server to, on full + // handshakes, send an alert just before reading the application data + // record to test False Start. This can be used in a negative False + // Start test to determine whether the peer processed the alert (and + // closed the connection) before or after sending app data. + AlertBeforeFalseStartTest alert + + // ExpectServerName, if not empty, is the hostname the client + // must specify in the server_name extension. + ExpectServerName string + + // SwapNPNAndALPN switches the relative order between NPN and ALPN in + // both ClientHello and ServerHello. + SwapNPNAndALPN bool + + // ALPNProtocol, if not nil, sets the ALPN protocol that a server will + // return. + ALPNProtocol *string + + // AcceptAnySession causes the server to resume sessions regardless of + // the version associated with the session or cipher suite. It also + // causes the server to look in both TLS 1.2 and 1.3 extensions to + // process a ticket. + AcceptAnySession bool + + // SendBothTickets, if true, causes the client to send tickets in both + // TLS 1.2 and 1.3 extensions. + SendBothTickets bool + + // FilterTicket, if not nil, causes the client to modify a session + // ticket before sending it in a resume handshake. + FilterTicket func([]byte) ([]byte, error) + + // TicketSessionIDLength, if non-zero, is the length of the session ID + // to send with a ticket resumption offer. + TicketSessionIDLength int + + // EmptyTicketSessionID, if true, causes the client to send an empty + // session ID with a ticket resumption offer. For simplicity, this will + // also cause the client to interpret a ServerHello with empty session + // ID as a resumption. (A client which sends empty session ID is + // normally expected to look ahead for ChangeCipherSpec.) + EmptyTicketSessionID bool + + // ExpectNoTLS12Session, if true, causes the server to fail the + // connection if either a session ID or TLS 1.2 ticket is offered. + ExpectNoTLS12Session bool + + // ExpectNoTLS13PSK, if true, causes the server to fail the connection + // if a TLS 1.3 PSK is offered. + ExpectNoTLS13PSK bool + + // RequireExtendedMasterSecret, if true, requires that the peer support + // the extended master secret option. + RequireExtendedMasterSecret bool + + // NoExtendedMasterSecret causes the client and server to behave as if + // they didn't support an extended master secret in the initial + // handshake. + NoExtendedMasterSecret bool + + // NoExtendedMasterSecretOnRenegotiation causes the client and server to + // behave as if they didn't support an extended master secret in + // renegotiation handshakes. + NoExtendedMasterSecretOnRenegotiation bool + + // EmptyRenegotiationInfo causes the renegotiation extension to be + // empty in a renegotiation handshake. + EmptyRenegotiationInfo bool + + // BadRenegotiationInfo causes the renegotiation extension value in a + // renegotiation handshake to be incorrect. + BadRenegotiationInfo bool + + // NoRenegotiationInfo disables renegotiation info support in all + // handshakes. + NoRenegotiationInfo bool + + // NoRenegotiationInfoInInitial disables renegotiation info support in + // the initial handshake. + NoRenegotiationInfoInInitial bool + + // NoRenegotiationInfoAfterInitial disables renegotiation info support + // in renegotiation handshakes. + NoRenegotiationInfoAfterInitial bool + + // RequireRenegotiationInfo, if true, causes the client to return an + // error if the server doesn't reply with the renegotiation extension. + RequireRenegotiationInfo bool + + // SequenceNumberMapping, if non-nil, is the mapping function to apply + // to the sequence number of outgoing packets. For both TLS and DTLS, + // the two most-significant bytes in the resulting sequence number are + // ignored so that the DTLS epoch cannot be changed. + SequenceNumberMapping func(uint64) uint64 + + // RSAEphemeralKey, if true, causes the server to send a + // ServerKeyExchange message containing an ephemeral key (as in + // RSA_EXPORT) in the plain RSA key exchange. + RSAEphemeralKey bool + + // SRTPMasterKeyIdentifer, if not empty, is the SRTP MKI value that the + // client offers when negotiating SRTP. MKI support is still missing so + // the peer must still send none. + SRTPMasterKeyIdentifer string + + // SendSRTPProtectionProfile, if non-zero, is the SRTP profile that the + // server sends in the ServerHello instead of the negotiated one. + SendSRTPProtectionProfile uint16 + + // NoSignatureAlgorithms, if true, causes the client to omit the + // signature and hashes extension. + // + // For a server, it will cause an empty list to be sent in the + // CertificateRequest message. None the less, the configured set will + // still be enforced. + NoSignatureAlgorithms bool + + // NoSupportedCurves, if true, causes the client to omit the + // supported_curves extension. + NoSupportedCurves bool + + // RequireSameRenegoClientVersion, if true, causes the server + // to require that all ClientHellos match in offered version + // across a renego. + RequireSameRenegoClientVersion bool + + // ExpectInitialRecordVersion, if non-zero, is the expected value of + // record-layer version field before the protocol version is determined. + ExpectInitialRecordVersion uint16 + + // SendRecordVersion, if non-zero, is the value to send as the + // record-layer version. + SendRecordVersion uint16 + + // SendInitialRecordVersion, if non-zero, is the value to send as the + // record-layer version before the protocol version is determined. + SendInitialRecordVersion uint16 + + // MaxPacketLength, if non-zero, is the maximum acceptable size for a + // packet. + MaxPacketLength int + + // SendCipherSuite, if non-zero, is the cipher suite value that the + // server will send in the ServerHello. This does not affect the cipher + // the server believes it has actually negotiated. + SendCipherSuite uint16 + + // SendCipherSuites, if not nil, is the cipher suite list that the + // client will send in the ClientHello. This does not affect the cipher + // the client believes it has actually offered. + SendCipherSuites []uint16 + + // AppDataBeforeHandshake, if not nil, causes application data to be + // sent immediately before the first handshake message. + AppDataBeforeHandshake []byte + + // AppDataAfterChangeCipherSpec, if not nil, causes application data to + // be sent immediately after ChangeCipherSpec. + AppDataAfterChangeCipherSpec []byte + + // AlertAfterChangeCipherSpec, if non-zero, causes an alert to be sent + // immediately after ChangeCipherSpec. + AlertAfterChangeCipherSpec alert + + // TimeoutSchedule is the schedule of packet drops and simulated + // timeouts for before each handshake leg from the peer. + TimeoutSchedule []time.Duration + + // PacketAdaptor is the packetAdaptor to use to simulate timeouts. + PacketAdaptor *packetAdaptor + + // ReorderHandshakeFragments, if true, causes handshake fragments in + // DTLS to overlap and be sent in the wrong order. It also causes + // pre-CCS flights to be sent twice. (Post-CCS flights consist of + // Finished and will trigger a spurious retransmit.) + ReorderHandshakeFragments bool + + // ReverseHandshakeFragments, if true, causes handshake fragments in + // DTLS to be reversed within a flight. + ReverseHandshakeFragments bool + + // MixCompleteMessageWithFragments, if true, causes handshake + // messages in DTLS to redundantly both fragment the message + // and include a copy of the full one. + MixCompleteMessageWithFragments bool + + // SendInvalidRecordType, if true, causes a record with an invalid + // content type to be sent immediately following the handshake. + SendInvalidRecordType bool + + // SendWrongMessageType, if non-zero, causes messages of the specified + // type to be sent with the wrong value. + SendWrongMessageType byte + + // SendTrailingMessageData, if non-zero, causes messages of the + // specified type to be sent with trailing data. + SendTrailingMessageData byte + + // FragmentMessageTypeMismatch, if true, causes all non-initial + // handshake fragments in DTLS to have the wrong message type. + FragmentMessageTypeMismatch bool + + // FragmentMessageLengthMismatch, if true, causes all non-initial + // handshake fragments in DTLS to have the wrong message length. + FragmentMessageLengthMismatch bool + + // SplitFragments, if non-zero, causes the handshake fragments in DTLS + // to be split across two records. The value of |SplitFragments| is the + // number of bytes in the first fragment. + SplitFragments int + + // SendEmptyFragments, if true, causes handshakes to include empty + // fragments in DTLS. + SendEmptyFragments bool + + // SendSplitAlert, if true, causes an alert to be sent with the header + // and record body split across multiple packets. The peer should + // discard these packets rather than process it. + SendSplitAlert bool + + // FailIfResumeOnRenego, if true, causes renegotiations to fail if the + // client offers a resumption or the server accepts one. + FailIfResumeOnRenego bool + + // IgnorePeerCipherPreferences, if true, causes the peer's cipher + // preferences to be ignored. + IgnorePeerCipherPreferences bool + + // IgnorePeerSignatureAlgorithmPreferences, if true, causes the peer's + // signature algorithm preferences to be ignored. + IgnorePeerSignatureAlgorithmPreferences bool + + // IgnorePeerCurvePreferences, if true, causes the peer's curve + // preferences to be ignored. + IgnorePeerCurvePreferences bool + + // BadFinished, if true, causes the Finished hash to be broken. + BadFinished bool + + // DHGroupPrime, if not nil, is used to define the (finite field) + // Diffie-Hellman group. The generator used is always two. + DHGroupPrime *big.Int + + // PackHandshakeFragments, if true, causes handshake fragments in DTLS + // to be packed into individual handshake records, up to the specified + // record size. + PackHandshakeFragments int + + // PackHandshakeRecords, if true, causes handshake records in DTLS to be + // packed into individual packets, up to the specified packet size. + PackHandshakeRecords int + + // PackHandshakeFlight, if true, causes each handshake flight in TLS to + // be packed into records, up to the largest size record available. + PackHandshakeFlight bool + + // AdvertiseAllConfiguredCiphers, if true, causes the client to + // advertise all configured cipher suite values. + AdvertiseAllConfiguredCiphers bool + + // EmptyCertificateList, if true, causes the server to send an empty + // certificate list in the Certificate message. + EmptyCertificateList bool + + // ExpectNewTicket, if true, causes the client to abort if it does not + // receive a new ticket. + ExpectNewTicket bool + + // RequireClientHelloSize, if not zero, is the required length in bytes + // of the ClientHello /record/. This is checked by the server. + RequireClientHelloSize int + + // CustomExtension, if not empty, contains the contents of an extension + // that will be added to client/server hellos. + CustomExtension string + + // CustomUnencryptedExtension, if not empty, contains the contents of + // an extension that will be added to ServerHello in TLS 1.3. + CustomUnencryptedExtension string + + // ExpectedCustomExtension, if not nil, contains the expected contents + // of a custom extension. + ExpectedCustomExtension *string + + // CustomTicketExtension, if not empty, contains the contents of an + // extension what will be added to NewSessionTicket in TLS 1.3. + CustomTicketExtension string + + // CustomTicketExtension, if not empty, contains the contents of an + // extension what will be added to HelloRetryRequest in TLS 1.3. + CustomHelloRetryRequestExtension string + + // NoCloseNotify, if true, causes the close_notify alert to be skipped + // on connection shutdown. + NoCloseNotify bool + + // SendAlertOnShutdown, if non-zero, is the alert to send instead of + // close_notify on shutdown. + SendAlertOnShutdown alert + + // ExpectCloseNotify, if true, requires a close_notify from the peer on + // shutdown. Records from the peer received after close_notify is sent + // are not discard. + ExpectCloseNotify bool + + // SendLargeRecords, if true, allows outgoing records to be sent + // arbitrarily large. + SendLargeRecords bool + + // NegotiateALPNAndNPN, if true, causes the server to negotiate both + // ALPN and NPN in the same connetion. + NegotiateALPNAndNPN bool + + // SendALPN, if non-empty, causes the server to send the specified + // string in the ALPN extension regardless of the content or presence of + // the client offer. + SendALPN string + + // SendUnencryptedALPN, if non-empty, causes the server to send the + // specified string in a ServerHello ALPN extension in TLS 1.3. + SendUnencryptedALPN string + + // SendEmptySessionTicket, if true, causes the server to send an empty + // session ticket. + SendEmptySessionTicket bool + + // SendPSKKeyExchangeModes, if present, determines the PSK key exchange modes + // to send. + SendPSKKeyExchangeModes []byte + + // ExpectNoNewSessionTicket, if present, means that the client will fail upon + // receipt of a NewSessionTicket message. + ExpectNoNewSessionTicket bool + + // DuplicateTicketEarlyDataInfo causes an extra empty extension of + // ticket_early_data_info to be sent in NewSessionTicket. + DuplicateTicketEarlyDataInfo bool + + // ExpectTicketEarlyDataInfo, if true, means that the client will fail upon + // absence of the ticket_early_data_info extension. + ExpectTicketEarlyDataInfo bool + + // ExpectTicketAge, if non-zero, is the expected age of the ticket that the + // server receives from the client. + ExpectTicketAge time.Duration + + // SendTicketAge, if non-zero, is the ticket age to be sent by the + // client. + SendTicketAge time.Duration + + // FailIfSessionOffered, if true, causes the server to fail any + // connections where the client offers a non-empty session ID or session + // ticket. + FailIfSessionOffered bool + + // SendHelloRequestBeforeEveryAppDataRecord, if true, causes a + // HelloRequest handshake message to be sent before each application + // data record. This only makes sense for a server. + SendHelloRequestBeforeEveryAppDataRecord bool + + // SendHelloRequestBeforeEveryHandshakeMessage, if true, causes a + // HelloRequest handshake message to be sent before each handshake + // message. This only makes sense for a server. + SendHelloRequestBeforeEveryHandshakeMessage bool + + // RequireDHPublicValueLen causes a fatal error if the length (in + // bytes) of the server's Diffie-Hellman public value is not equal to + // this. + RequireDHPublicValueLen int + + // BadChangeCipherSpec, if not nil, is the body to be sent in + // ChangeCipherSpec records instead of {1}. + BadChangeCipherSpec []byte + + // BadHelloRequest, if not nil, is what to send instead of a + // HelloRequest. + BadHelloRequest []byte + + // RequireSessionTickets, if true, causes the client to require new + // sessions use session tickets instead of session IDs. + RequireSessionTickets bool + + // NullAllCiphers, if true, causes every cipher to behave like the null + // cipher. + NullAllCiphers bool + + // SendSCTListOnResume, if not nil, causes the server to send the + // supplied SCT list in resumption handshakes. + SendSCTListOnResume []byte + + // SendOCSPResponseOnResume, if not nil, causes the server to advertise + // OCSP stapling in resumption handshakes and, if applicable, send the + // supplied stapled response. + SendOCSPResponseOnResume []byte + + // SendExtensionOnCertificate, if not nil, causes the runner to send the + // supplied bytes in the extensions on the Certificate message. + SendExtensionOnCertificate []byte + + // SendOCSPOnIntermediates, if not nil, causes the server to send the + // supplied OCSP on intermediate certificates in the Certificate message. + SendOCSPOnIntermediates []byte + + // SendSCTOnIntermediates, if not nil, causes the server to send the + // supplied SCT on intermediate certificates in the Certificate message. + SendSCTOnIntermediates []byte + + // SendDuplicateCertExtensions, if true, causes the server to send an extra + // copy of the OCSP/SCT extensions in the Certificate message. + SendDuplicateCertExtensions bool + + // ExpectNoExtensionsOnIntermediate, if true, causes the client to + // reject extensions on intermediate certificates. + ExpectNoExtensionsOnIntermediate bool + + // RecordPadding is the number of bytes of padding to add to each + // encrypted record in TLS 1.3. + RecordPadding int + + // OmitRecordContents, if true, causes encrypted records in TLS 1.3 to + // be missing their body and content type. Padding, if configured, is + // still added. + OmitRecordContents bool + + // OuterRecordType, if non-zero, is the outer record type to use instead + // of application data. + OuterRecordType recordType + + // SendSignatureAlgorithm, if non-zero, causes all signatures to be sent + // with the given signature algorithm rather than the one negotiated. + SendSignatureAlgorithm signatureAlgorithm + + // SkipECDSACurveCheck, if true, causes all ECDSA curve checks to be + // skipped. + SkipECDSACurveCheck bool + + // IgnoreSignatureVersionChecks, if true, causes all signature + // algorithms to be enabled at all TLS versions. + IgnoreSignatureVersionChecks bool + + // NegotiateRenegotiationInfoAtAllVersions, if true, causes + // Renegotiation Info to be negotiated at all versions. + NegotiateRenegotiationInfoAtAllVersions bool + + // NegotiateNPNAtAllVersions, if true, causes NPN to be negotiated at + // all versions. + NegotiateNPNAtAllVersions bool + + // NegotiateEMSAtAllVersions, if true, causes EMS to be negotiated at + // all versions. + NegotiateEMSAtAllVersions bool + + // AdvertiseTicketExtension, if true, causes the ticket extension to be + // advertised in server extensions + AdvertiseTicketExtension bool + + // NegotiatePSKResumption, if true, causes the server to attempt pure PSK + // resumption. + NegotiatePSKResumption bool + + // AlwaysSelectPSKIdentity, if true, causes the server in TLS 1.3 to + // always acknowledge a session, regardless of one was offered. + AlwaysSelectPSKIdentity bool + + // SelectPSKIdentityOnResume, if non-zero, causes the server to select + // the specified PSK identity index rather than the actual value. + SelectPSKIdentityOnResume uint16 + + // ExtraPSKIdentity, if true, causes the client to send an extra PSK + // identity. + ExtraPSKIdentity bool + + // MissingKeyShare, if true, causes the TLS 1.3 implementation to skip + // sending a key_share extension and use the zero ECDHE secret + // instead. + MissingKeyShare bool + + // SecondClientHelloMissingKeyShare, if true, causes the second TLS 1.3 + // ClientHello to skip sending a key_share extension and use the zero + // ECDHE secret instead. + SecondClientHelloMissingKeyShare bool + + // MisinterpretHelloRetryRequestCurve, if non-zero, causes the TLS 1.3 + // client to pretend the server requested a HelloRetryRequest with the + // given curve rather than the actual one. + MisinterpretHelloRetryRequestCurve CurveID + + // DuplicateKeyShares, if true, causes the TLS 1.3 client to send two + // copies of each KeyShareEntry. + DuplicateKeyShares bool + + // SendEarlyAlert, if true, sends a fatal alert after the ClientHello. + SendEarlyAlert bool + + // SendFakeEarlyDataLength, if non-zero, is the amount of early data to + // send after the ClientHello. + SendFakeEarlyDataLength int + + // OmitEarlyDataExtension, if true, causes the early data extension to + // be omitted in the ClientHello. + OmitEarlyDataExtension bool + + // SendEarlyDataOnSecondClientHello, if true, causes the TLS 1.3 client to + // send early data after the second ClientHello. + SendEarlyDataOnSecondClientHello bool + + // InterleaveEarlyData, if true, causes the TLS 1.3 client to send early + // data interleaved with the second ClientHello and the client Finished. + InterleaveEarlyData bool + + // SendEarlyData causes a TLS 1.3 client to send the provided data + // in application data records immediately after the ClientHello, + // provided that the client has a PSK that is appropriate for sending + // early data and includes that PSK in its ClientHello. + SendEarlyData [][]byte + + // ExpectEarlyData causes a TLS 1.3 server to read application + // data after the ClientHello (assuming the server is able to + // derive the key under which the data is encrypted) before it + // sends a ServerHello. It checks that the application data it + // reads matches what is provided in ExpectEarlyData and errors if + // the number of records or their content do not match. + ExpectEarlyData [][]byte + + // SendHalfRTTData causes a TLS 1.3 server to send the provided + // data in application data records before reading the client's + // Finished message. + SendHalfRTTData [][]byte + + // ExpectHalfRTTData causes a TLS 1.3 client to read application + // data after reading the server's Finished message and before + // sending any other handshake messages. It checks that the + // application data it reads matches what is provided in + // ExpectHalfRTTData and errors if the number of records or their + // content do not match. + ExpectHalfRTTData [][]byte + + // EmptyEncryptedExtensions, if true, causes the TLS 1.3 server to + // emit an empty EncryptedExtensions block. + EmptyEncryptedExtensions bool + + // EncryptedExtensionsWithKeyShare, if true, causes the TLS 1.3 server to + // include the KeyShare extension in the EncryptedExtensions block. + EncryptedExtensionsWithKeyShare bool + + // AlwaysSendHelloRetryRequest, if true, causes a HelloRetryRequest to + // be sent by the server, even if empty. + AlwaysSendHelloRetryRequest bool + + // SecondHelloRetryRequest, if true, causes the TLS 1.3 server to send + // two HelloRetryRequests instead of one. + SecondHelloRetryRequest bool + + // SendHelloRetryRequestCurve, if non-zero, causes the server to send + // the specified curve in a HelloRetryRequest. + SendHelloRetryRequestCurve CurveID + + // SendHelloRetryRequestCookie, if not nil, contains a cookie to be + // sent by the server in HelloRetryRequest. + SendHelloRetryRequestCookie []byte + + // DuplicateHelloRetryRequestExtensions, if true, causes all + // HelloRetryRequest extensions to be sent twice. + DuplicateHelloRetryRequestExtensions bool + + // SendServerHelloVersion, if non-zero, causes the server to send the + // specified value in ServerHello version field. + SendServerHelloVersion uint16 + + // SkipHelloRetryRequest, if true, causes the TLS 1.3 server to not send + // HelloRetryRequest. + SkipHelloRetryRequest bool + + // PackHelloRequestWithFinished, if true, causes the TLS server to send + // HelloRequest in the same record as Finished. + PackHelloRequestWithFinished bool + + // ExpectMissingKeyShare, if true, causes the TLS server to fail the + // connection if the selected curve appears in the client's initial + // ClientHello. That is, it requires that a HelloRetryRequest be sent. + ExpectMissingKeyShare bool + + // SendExtraFinished, if true, causes an extra Finished message to be + // sent. + SendExtraFinished bool + + // SendRequestContext, if not empty, is the request context to send in + // a TLS 1.3 CertificateRequest. + SendRequestContext []byte + + // SendSNIWarningAlert, if true, causes the server to send an + // unrecognized_name alert before the ServerHello. + SendSNIWarningAlert bool + + // SendCompressionMethods, if not nil, is the compression method list to + // send in the ClientHello. + SendCompressionMethods []byte + + // AlwaysSendPreSharedKeyIdentityHint, if true, causes the server to + // always send a ServerKeyExchange for PSK ciphers, even if the identity + // hint is empty. + AlwaysSendPreSharedKeyIdentityHint bool + + // TrailingKeyShareData, if true, causes the client key share list to + // include a trailing byte. + TrailingKeyShareData bool + + // InvalidChannelIDSignature, if true, causes the client to generate an + // invalid Channel ID signature. + InvalidChannelIDSignature bool + + // ExpectGREASE, if true, causes messages without GREASE values to be + // rejected. See draft-davidben-tls-grease-01. + ExpectGREASE bool + + // SendShortPSKBinder, if true, causes the client to send a PSK binder + // that is one byte shorter than it should be. + SendShortPSKBinder bool + + // SendInvalidPSKBinder, if true, causes the client to send an invalid + // PSK binder. + SendInvalidPSKBinder bool + + // SendNoPSKBinder, if true, causes the client to send no PSK binders. + SendNoPSKBinder bool + + // SendExtraPSKBinder, if true, causes the client to send an extra PSK + // binder. + SendExtraPSKBinder bool + + // PSKBinderFirst, if true, causes the client to send the PSK Binder + // extension as the first extension instead of the last extension. + PSKBinderFirst bool + + // NoOCSPStapling, if true, causes the client to not request OCSP + // stapling. + NoOCSPStapling bool + + // NoSignedCertificateTimestamps, if true, causes the client to not + // request signed certificate timestamps. + NoSignedCertificateTimestamps bool + + // SendSupportedPointFormats, if not nil, is the list of supported point + // formats to send in ClientHello or ServerHello. If set to a non-nil + // empty slice, no extension will be sent. + SendSupportedPointFormats []byte + + // MaxReceivePlaintext, if non-zero, is the maximum plaintext record + // length accepted from the peer. + MaxReceivePlaintext int + + // SendTicketLifetime, if non-zero, is the ticket lifetime to send in + // NewSessionTicket messages. + SendTicketLifetime time.Duration + + // SendServerNameAck, if true, causes the server to acknowledge the SNI + // extension. + SendServerNameAck bool + + // ExpectCertificateReqNames, if not nil, contains the list of X.509 + // names that must be sent in a CertificateRequest from the server. + ExpectCertificateReqNames [][]byte + + // RenegotiationCertificate, if not nil, is the certificate to use on + // renegotiation handshakes. + RenegotiationCertificate *Certificate +} + +func (c *Config) serverInit() { + if c.SessionTicketsDisabled { + return + } + + // If the key has already been set then we have nothing to do. + for _, b := range c.SessionTicketKey { + if b != 0 { + return + } + } + + if _, err := io.ReadFull(c.rand(), c.SessionTicketKey[:]); err != nil { + c.SessionTicketsDisabled = true + } +} + +func (c *Config) rand() io.Reader { + r := c.Rand + if r == nil { + return rand.Reader + } + return r +} + +func (c *Config) time() time.Time { + t := c.Time + if t == nil { + t = time.Now + } + return t() +} + +func (c *Config) cipherSuites() []uint16 { + s := c.CipherSuites + if s == nil { + s = defaultCipherSuites() + } + return s +} + +func (c *Config) minVersion(isDTLS bool) uint16 { + ret := uint16(minVersion) + if c != nil && c.MinVersion != 0 { + ret = c.MinVersion + } + if isDTLS { + // The lowest version of DTLS is 1.0. There is no DSSL 3.0. + if ret < VersionTLS10 { + return VersionTLS10 + } + // There is no such thing as DTLS 1.1. + if ret == VersionTLS11 { + return VersionTLS12 + } + } + return ret +} + +func (c *Config) maxVersion(isDTLS bool) uint16 { + ret := uint16(maxVersion) + if c != nil && c.MaxVersion != 0 { + ret = c.MaxVersion + } + if isDTLS { + // We only implement up to DTLS 1.2. + if ret > VersionTLS12 { + return VersionTLS12 + } + // There is no such thing as DTLS 1.1. + if ret == VersionTLS11 { + return VersionTLS10 + } + } + return ret +} + +var defaultCurvePreferences = []CurveID{CurveX25519, CurveP256, CurveP384, CurveP521} + +func (c *Config) curvePreferences() []CurveID { + if c == nil || len(c.CurvePreferences) == 0 { + return defaultCurvePreferences + } + return c.CurvePreferences +} + +func (c *Config) defaultCurves() map[CurveID]bool { + defaultCurves := make(map[CurveID]bool) + curves := c.DefaultCurves + if c == nil || c.DefaultCurves == nil { + curves = c.curvePreferences() + } + for _, curveID := range curves { + defaultCurves[curveID] = true + } + return defaultCurves +} + +// isSupportedVersion returns true if the specified protocol version is +// acceptable. +func (c *Config) isSupportedVersion(vers uint16, isDTLS bool) bool { + return c.minVersion(isDTLS) <= vers && vers <= c.maxVersion(isDTLS) +} + +// getCertificateForName returns the best certificate for the given name, +// defaulting to the first element of c.Certificates if there are no good +// options. +func (c *Config) getCertificateForName(name string) *Certificate { + if len(c.Certificates) == 1 || c.NameToCertificate == nil { + // There's only one choice, so no point doing any work. + return &c.Certificates[0] + } + + name = strings.ToLower(name) + for len(name) > 0 && name[len(name)-1] == '.' { + name = name[:len(name)-1] + } + + if cert, ok := c.NameToCertificate[name]; ok { + return cert + } + + // try replacing labels in the name with wildcards until we get a + // match. + labels := strings.Split(name, ".") + for i := range labels { + labels[i] = "*" + candidate := strings.Join(labels, ".") + if cert, ok := c.NameToCertificate[candidate]; ok { + return cert + } + } + + // If nothing matches, return the first certificate. + return &c.Certificates[0] +} + +func (c *Config) signSignatureAlgorithms() []signatureAlgorithm { + if c != nil && c.SignSignatureAlgorithms != nil { + return c.SignSignatureAlgorithms + } + return supportedSignatureAlgorithms +} + +func (c *Config) verifySignatureAlgorithms() []signatureAlgorithm { + if c != nil && c.VerifySignatureAlgorithms != nil { + return c.VerifySignatureAlgorithms + } + return supportedSignatureAlgorithms +} + +// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate +// from the CommonName and SubjectAlternateName fields of each of the leaf +// certificates. +func (c *Config) BuildNameToCertificate() { + c.NameToCertificate = make(map[string]*Certificate) + for i := range c.Certificates { + cert := &c.Certificates[i] + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + continue + } + if len(x509Cert.Subject.CommonName) > 0 { + c.NameToCertificate[x509Cert.Subject.CommonName] = cert + } + for _, san := range x509Cert.DNSNames { + c.NameToCertificate[san] = cert + } + } +} + +// A Certificate is a chain of one or more certificates, leaf first. +type Certificate struct { + Certificate [][]byte + PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey + // OCSPStaple contains an optional OCSP response which will be served + // to clients that request it. + OCSPStaple []byte + // SignedCertificateTimestampList contains an optional encoded + // SignedCertificateTimestampList structure which will be + // served to clients that request it. + SignedCertificateTimestampList []byte + // Leaf is the parsed form of the leaf certificate, which may be + // initialized using x509.ParseCertificate to reduce per-handshake + // processing for TLS clients doing client authentication. If nil, the + // leaf certificate will be parsed as needed. + Leaf *x509.Certificate +} + +// A TLS record. +type record struct { + contentType recordType + major, minor uint8 + payload []byte +} + +type handshakeMessage interface { + marshal() []byte + unmarshal([]byte) bool +} + +// lruSessionCache is a client or server session cache implementation +// that uses an LRU caching strategy. +type lruSessionCache struct { + sync.Mutex + + m map[string]*list.Element + q *list.List + capacity int +} + +type lruSessionCacheEntry struct { + sessionKey string + state interface{} +} + +// Put adds the provided (sessionKey, cs) pair to the cache. +func (c *lruSessionCache) Put(sessionKey string, cs interface{}) { + c.Lock() + defer c.Unlock() + + if elem, ok := c.m[sessionKey]; ok { + entry := elem.Value.(*lruSessionCacheEntry) + entry.state = cs + c.q.MoveToFront(elem) + return + } + + if c.q.Len() < c.capacity { + entry := &lruSessionCacheEntry{sessionKey, cs} + c.m[sessionKey] = c.q.PushFront(entry) + return + } + + elem := c.q.Back() + entry := elem.Value.(*lruSessionCacheEntry) + delete(c.m, entry.sessionKey) + entry.sessionKey = sessionKey + entry.state = cs + c.q.MoveToFront(elem) + c.m[sessionKey] = elem +} + +// Get returns the value associated with a given key. It returns (nil, +// false) if no value is found. +func (c *lruSessionCache) Get(sessionKey string) (interface{}, bool) { + c.Lock() + defer c.Unlock() + + if elem, ok := c.m[sessionKey]; ok { + c.q.MoveToFront(elem) + return elem.Value.(*lruSessionCacheEntry).state, true + } + return nil, false +} + +// lruClientSessionCache is a ClientSessionCache implementation that +// uses an LRU caching strategy. +type lruClientSessionCache struct { + lruSessionCache +} + +func (c *lruClientSessionCache) Put(sessionKey string, cs *ClientSessionState) { + c.lruSessionCache.Put(sessionKey, cs) +} + +func (c *lruClientSessionCache) Get(sessionKey string) (*ClientSessionState, bool) { + cs, ok := c.lruSessionCache.Get(sessionKey) + if !ok { + return nil, false + } + return cs.(*ClientSessionState), true +} + +// lruServerSessionCache is a ServerSessionCache implementation that +// uses an LRU caching strategy. +type lruServerSessionCache struct { + lruSessionCache +} + +func (c *lruServerSessionCache) Put(sessionId string, session *sessionState) { + c.lruSessionCache.Put(sessionId, session) +} + +func (c *lruServerSessionCache) Get(sessionId string) (*sessionState, bool) { + cs, ok := c.lruSessionCache.Get(sessionId) + if !ok { + return nil, false + } + return cs.(*sessionState), true +} + +// NewLRUClientSessionCache returns a ClientSessionCache with the given +// capacity that uses an LRU strategy. If capacity is < 1, a default capacity +// is used instead. +func NewLRUClientSessionCache(capacity int) ClientSessionCache { + const defaultSessionCacheCapacity = 64 + + if capacity < 1 { + capacity = defaultSessionCacheCapacity + } + return &lruClientSessionCache{ + lruSessionCache{ + m: make(map[string]*list.Element), + q: list.New(), + capacity: capacity, + }, + } +} + +// NewLRUServerSessionCache returns a ServerSessionCache with the given +// capacity that uses an LRU strategy. If capacity is < 1, a default capacity +// is used instead. +func NewLRUServerSessionCache(capacity int) ServerSessionCache { + const defaultSessionCacheCapacity = 64 + + if capacity < 1 { + capacity = defaultSessionCacheCapacity + } + return &lruServerSessionCache{ + lruSessionCache{ + m: make(map[string]*list.Element), + q: list.New(), + capacity: capacity, + }, + } +} + +// TODO(jsing): Make these available to both crypto/x509 and crypto/tls. +type dsaSignature struct { + R, S *big.Int +} + +type ecdsaSignature dsaSignature + +var emptyConfig Config + +func defaultConfig() *Config { + return &emptyConfig +} + +var ( + once sync.Once + varDefaultCipherSuites []uint16 +) + +func defaultCipherSuites() []uint16 { + once.Do(initDefaultCipherSuites) + return varDefaultCipherSuites +} + +func initDefaultCipherSuites() { + for _, suite := range cipherSuites { + if suite.flags&suitePSK == 0 { + varDefaultCipherSuites = append(varDefaultCipherSuites, suite.id) + } + } +} + +func unexpectedMessageError(wanted, got interface{}) error { + return fmt.Errorf("tls: received unexpected handshake message of type %T when waiting for %T", got, wanted) +} + +func isSupportedSignatureAlgorithm(sigAlg signatureAlgorithm, sigAlgs []signatureAlgorithm) bool { + for _, s := range sigAlgs { + if s == sigAlg { + return true + } + } + return false +} + +var ( + // See draft-ietf-tls-tls13-16, section 6.3.1.2. + downgradeTLS13 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01} + downgradeTLS12 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00} +) diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/conn.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/conn.go new file mode 100644 index 000000000..1bdca84de --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/conn.go @@ -0,0 +1,1843 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TLS low level connection and record layer + +package runner + +import ( + "bytes" + "crypto/cipher" + "crypto/ecdsa" + "crypto/subtle" + "crypto/x509" + "encoding/binary" + "errors" + "fmt" + "io" + "net" + "sync" + "time" +) + +var errNoCertificateAlert = errors.New("tls: no certificate alert") +var errEndOfEarlyDataAlert = errors.New("tls: end of early data alert") + +// A Conn represents a secured connection. +// It implements the net.Conn interface. +type Conn struct { + // constant + conn net.Conn + isDTLS bool + isClient bool + + // constant after handshake; protected by handshakeMutex + handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex + handshakeErr error // error resulting from handshake + vers uint16 // TLS version + haveVers bool // version has been negotiated + config *Config // configuration passed to constructor + handshakeComplete bool + skipEarlyData bool // On a server, indicates that the client is sending early data that must be skipped over. + didResume bool // whether this connection was a session resumption + extendedMasterSecret bool // whether this session used an extended master secret + cipherSuite *cipherSuite + ocspResponse []byte // stapled OCSP response + sctList []byte // signed certificate timestamp list + peerCertificates []*x509.Certificate + // verifiedChains contains the certificate chains that we built, as + // opposed to the ones presented by the server. + verifiedChains [][]*x509.Certificate + // serverName contains the server name indicated by the client, if any. + serverName string + // firstFinished contains the first Finished hash sent during the + // handshake. This is the "tls-unique" channel binding value. + firstFinished [12]byte + // peerSignatureAlgorithm contains the signature algorithm that was used + // by the peer in the handshake, or zero if not applicable. + peerSignatureAlgorithm signatureAlgorithm + // curveID contains the curve that was used in the handshake, or zero if + // not applicable. + curveID CurveID + + clientRandom, serverRandom [32]byte + exporterSecret []byte + resumptionSecret []byte + + clientProtocol string + clientProtocolFallback bool + usedALPN bool + + // verify_data values for the renegotiation extension. + clientVerify []byte + serverVerify []byte + + channelID *ecdsa.PublicKey + + srtpProtectionProfile uint16 + + clientVersion uint16 + + // input/output + in, out halfConn // in.Mutex < out.Mutex + rawInput *block // raw input, right off the wire + input *block // application record waiting to be read + hand bytes.Buffer // handshake record waiting to be read + + // pendingFlight, if PackHandshakeFlight is enabled, is the buffer of + // handshake data to be split into records at the end of the flight. + pendingFlight bytes.Buffer + + // DTLS state + sendHandshakeSeq uint16 + recvHandshakeSeq uint16 + handMsg []byte // pending assembled handshake message + handMsgLen int // handshake message length, not including the header + pendingFragments [][]byte // pending outgoing handshake fragments. + + keyUpdateRequested bool + + tmp [16]byte +} + +func (c *Conn) init() { + c.in.isDTLS = c.isDTLS + c.out.isDTLS = c.isDTLS + c.in.config = c.config + c.out.config = c.config + + c.out.updateOutSeq() +} + +// Access to net.Conn methods. +// Cannot just embed net.Conn because that would +// export the struct field too. + +// LocalAddr returns the local network address. +func (c *Conn) LocalAddr() net.Addr { + return c.conn.LocalAddr() +} + +// RemoteAddr returns the remote network address. +func (c *Conn) RemoteAddr() net.Addr { + return c.conn.RemoteAddr() +} + +// SetDeadline sets the read and write deadlines associated with the connection. +// A zero value for t means Read and Write will not time out. +// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. +func (c *Conn) SetDeadline(t time.Time) error { + return c.conn.SetDeadline(t) +} + +// SetReadDeadline sets the read deadline on the underlying connection. +// A zero value for t means Read will not time out. +func (c *Conn) SetReadDeadline(t time.Time) error { + return c.conn.SetReadDeadline(t) +} + +// SetWriteDeadline sets the write deadline on the underlying conneciton. +// A zero value for t means Write will not time out. +// After a Write has timed out, the TLS state is corrupt and all future writes will return the same error. +func (c *Conn) SetWriteDeadline(t time.Time) error { + return c.conn.SetWriteDeadline(t) +} + +// A halfConn represents one direction of the record layer +// connection, either sending or receiving. +type halfConn struct { + sync.Mutex + + err error // first permanent error + version uint16 // protocol version + isDTLS bool + cipher interface{} // cipher algorithm + mac macFunction + seq [8]byte // 64-bit sequence number + outSeq [8]byte // Mapped sequence number + bfree *block // list of free blocks + + nextCipher interface{} // next encryption state + nextMac macFunction // next MAC algorithm + nextSeq [6]byte // next epoch's starting sequence number in DTLS + + // used to save allocating a new buffer for each MAC. + inDigestBuf, outDigestBuf []byte + + trafficSecret []byte + + config *Config +} + +func (hc *halfConn) setErrorLocked(err error) error { + hc.err = err + return err +} + +func (hc *halfConn) error() error { + // This should be locked, but I've removed it for the renegotiation + // tests since we don't concurrently read and write the same tls.Conn + // in any case during testing. + err := hc.err + return err +} + +// prepareCipherSpec sets the encryption and MAC states +// that a subsequent changeCipherSpec will use. +func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) { + hc.version = version + hc.nextCipher = cipher + hc.nextMac = mac +} + +// changeCipherSpec changes the encryption and MAC states +// to the ones previously passed to prepareCipherSpec. +func (hc *halfConn) changeCipherSpec(config *Config) error { + if hc.nextCipher == nil { + return alertInternalError + } + hc.cipher = hc.nextCipher + hc.mac = hc.nextMac + hc.nextCipher = nil + hc.nextMac = nil + hc.config = config + hc.incEpoch() + + if config.Bugs.NullAllCiphers { + hc.cipher = nullCipher{} + hc.mac = nil + } + return nil +} + +// useTrafficSecret sets the current cipher state for TLS 1.3. +func (hc *halfConn) useTrafficSecret(version uint16, suite *cipherSuite, secret []byte, side trafficDirection) { + hc.version = version + hc.cipher = deriveTrafficAEAD(version, suite, secret, side) + if hc.config.Bugs.NullAllCiphers { + hc.cipher = nullCipher{} + } + hc.trafficSecret = secret + hc.incEpoch() +} + +// resetCipher changes the cipher state back to no encryption to be able +// to send an unencrypted ClientHello in response to HelloRetryRequest +// after 0-RTT data was rejected. +func (hc *halfConn) resetCipher() { + hc.cipher = nil + hc.incEpoch() +} + +func (hc *halfConn) doKeyUpdate(c *Conn, isOutgoing bool) { + side := serverWrite + if c.isClient == isOutgoing { + side = clientWrite + } + hc.useTrafficSecret(hc.version, c.cipherSuite, updateTrafficSecret(c.cipherSuite.hash(), hc.trafficSecret), side) +} + +// incSeq increments the sequence number. +func (hc *halfConn) incSeq(isOutgoing bool) { + limit := 0 + increment := uint64(1) + if hc.isDTLS { + // Increment up to the epoch in DTLS. + limit = 2 + } + for i := 7; i >= limit; i-- { + increment += uint64(hc.seq[i]) + hc.seq[i] = byte(increment) + increment >>= 8 + } + + // Not allowed to let sequence number wrap. + // Instead, must renegotiate before it does. + // Not likely enough to bother. + if increment != 0 { + panic("TLS: sequence number wraparound") + } + + hc.updateOutSeq() +} + +// incNextSeq increments the starting sequence number for the next epoch. +func (hc *halfConn) incNextSeq() { + for i := len(hc.nextSeq) - 1; i >= 0; i-- { + hc.nextSeq[i]++ + if hc.nextSeq[i] != 0 { + return + } + } + panic("TLS: sequence number wraparound") +} + +// incEpoch resets the sequence number. In DTLS, it also increments the epoch +// half of the sequence number. +func (hc *halfConn) incEpoch() { + if hc.isDTLS { + for i := 1; i >= 0; i-- { + hc.seq[i]++ + if hc.seq[i] != 0 { + break + } + if i == 0 { + panic("TLS: epoch number wraparound") + } + } + copy(hc.seq[2:], hc.nextSeq[:]) + for i := range hc.nextSeq { + hc.nextSeq[i] = 0 + } + } else { + for i := range hc.seq { + hc.seq[i] = 0 + } + } + + hc.updateOutSeq() +} + +func (hc *halfConn) updateOutSeq() { + if hc.config.Bugs.SequenceNumberMapping != nil { + seqU64 := binary.BigEndian.Uint64(hc.seq[:]) + seqU64 = hc.config.Bugs.SequenceNumberMapping(seqU64) + binary.BigEndian.PutUint64(hc.outSeq[:], seqU64) + + // The DTLS epoch cannot be changed. + copy(hc.outSeq[:2], hc.seq[:2]) + return + } + + copy(hc.outSeq[:], hc.seq[:]) +} + +func (hc *halfConn) recordHeaderLen() int { + if hc.isDTLS { + return dtlsRecordHeaderLen + } + return tlsRecordHeaderLen +} + +// removePadding returns an unpadded slice, in constant time, which is a prefix +// of the input. It also returns a byte which is equal to 255 if the padding +// was valid and 0 otherwise. See RFC 2246, section 6.2.3.2 +func removePadding(payload []byte) ([]byte, byte) { + if len(payload) < 1 { + return payload, 0 + } + + paddingLen := payload[len(payload)-1] + t := uint(len(payload)-1) - uint(paddingLen) + // if len(payload) >= (paddingLen - 1) then the MSB of t is zero + good := byte(int32(^t) >> 31) + + toCheck := 255 // the maximum possible padding length + // The length of the padded data is public, so we can use an if here + if toCheck+1 > len(payload) { + toCheck = len(payload) - 1 + } + + for i := 0; i < toCheck; i++ { + t := uint(paddingLen) - uint(i) + // if i <= paddingLen then the MSB of t is zero + mask := byte(int32(^t) >> 31) + b := payload[len(payload)-1-i] + good &^= mask&paddingLen ^ mask&b + } + + // We AND together the bits of good and replicate the result across + // all the bits. + good &= good << 4 + good &= good << 2 + good &= good << 1 + good = uint8(int8(good) >> 7) + + toRemove := good&paddingLen + 1 + return payload[:len(payload)-int(toRemove)], good +} + +// removePaddingSSL30 is a replacement for removePadding in the case that the +// protocol version is SSLv3. In this version, the contents of the padding +// are random and cannot be checked. +func removePaddingSSL30(payload []byte) ([]byte, byte) { + if len(payload) < 1 { + return payload, 0 + } + + paddingLen := int(payload[len(payload)-1]) + 1 + if paddingLen > len(payload) { + return payload, 0 + } + + return payload[:len(payload)-paddingLen], 255 +} + +func roundUp(a, b int) int { + return a + (b-a%b)%b +} + +// cbcMode is an interface for block ciphers using cipher block chaining. +type cbcMode interface { + cipher.BlockMode + SetIV([]byte) +} + +// decrypt checks and strips the mac and decrypts the data in b. Returns a +// success boolean, the number of bytes to skip from the start of the record in +// order to get the application payload, the encrypted record type (or 0 +// if there is none), and an optional alert value. +func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, contentType recordType, alertValue alert) { + recordHeaderLen := hc.recordHeaderLen() + + // pull out payload + payload := b.data[recordHeaderLen:] + + macSize := 0 + if hc.mac != nil { + macSize = hc.mac.Size() + } + + paddingGood := byte(255) + explicitIVLen := 0 + + seq := hc.seq[:] + if hc.isDTLS { + // DTLS sequence numbers are explicit. + seq = b.data[3:11] + } + + // decrypt + if hc.cipher != nil { + switch c := hc.cipher.(type) { + case cipher.Stream: + c.XORKeyStream(payload, payload) + case *tlsAead: + nonce := seq + if c.explicitNonce { + explicitIVLen = 8 + if len(payload) < explicitIVLen { + return false, 0, 0, alertBadRecordMAC + } + nonce = payload[:8] + payload = payload[8:] + } + + var additionalData []byte + if hc.version < VersionTLS13 { + additionalData = make([]byte, 13) + copy(additionalData, seq) + copy(additionalData[8:], b.data[:3]) + n := len(payload) - c.Overhead() + additionalData[11] = byte(n >> 8) + additionalData[12] = byte(n) + } + var err error + payload, err = c.Open(payload[:0], nonce, payload, additionalData) + if err != nil { + return false, 0, 0, alertBadRecordMAC + } + b.resize(recordHeaderLen + explicitIVLen + len(payload)) + case cbcMode: + blockSize := c.BlockSize() + if hc.version >= VersionTLS11 || hc.isDTLS { + explicitIVLen = blockSize + } + + if len(payload)%blockSize != 0 || len(payload) < roundUp(explicitIVLen+macSize+1, blockSize) { + return false, 0, 0, alertBadRecordMAC + } + + if explicitIVLen > 0 { + c.SetIV(payload[:explicitIVLen]) + payload = payload[explicitIVLen:] + } + c.CryptBlocks(payload, payload) + if hc.version == VersionSSL30 { + payload, paddingGood = removePaddingSSL30(payload) + } else { + payload, paddingGood = removePadding(payload) + } + b.resize(recordHeaderLen + explicitIVLen + len(payload)) + + // note that we still have a timing side-channel in the + // MAC check, below. An attacker can align the record + // so that a correct padding will cause one less hash + // block to be calculated. Then they can iteratively + // decrypt a record by breaking each byte. See + // "Password Interception in a SSL/TLS Channel", Brice + // Canvel et al. + // + // However, our behavior matches OpenSSL, so we leak + // only as much as they do. + case nullCipher: + break + default: + panic("unknown cipher type") + } + + if hc.version >= VersionTLS13 { + i := len(payload) + for i > 0 && payload[i-1] == 0 { + i-- + } + payload = payload[:i] + if len(payload) == 0 { + return false, 0, 0, alertUnexpectedMessage + } + contentType = recordType(payload[len(payload)-1]) + payload = payload[:len(payload)-1] + b.resize(recordHeaderLen + len(payload)) + } + } + + // check, strip mac + if hc.mac != nil { + if len(payload) < macSize { + return false, 0, 0, alertBadRecordMAC + } + + // strip mac off payload, b.data + n := len(payload) - macSize + b.data[recordHeaderLen-2] = byte(n >> 8) + b.data[recordHeaderLen-1] = byte(n) + b.resize(recordHeaderLen + explicitIVLen + n) + remoteMAC := payload[n:] + localMAC := hc.mac.MAC(hc.inDigestBuf, seq, b.data[:3], b.data[recordHeaderLen-2:recordHeaderLen], payload[:n]) + + if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { + return false, 0, 0, alertBadRecordMAC + } + hc.inDigestBuf = localMAC + } + hc.incSeq(false) + + return true, recordHeaderLen + explicitIVLen, contentType, 0 +} + +// padToBlockSize calculates the needed padding block, if any, for a payload. +// On exit, prefix aliases payload and extends to the end of the last full +// block of payload. finalBlock is a fresh slice which contains the contents of +// any suffix of payload as well as the needed padding to make finalBlock a +// full block. +func padToBlockSize(payload []byte, blockSize int, config *Config) (prefix, finalBlock []byte) { + overrun := len(payload) % blockSize + prefix = payload[:len(payload)-overrun] + + paddingLen := blockSize - overrun + finalSize := blockSize + if config.Bugs.MaxPadding { + for paddingLen+blockSize <= 256 { + paddingLen += blockSize + } + finalSize = 256 + } + finalBlock = make([]byte, finalSize) + for i := range finalBlock { + finalBlock[i] = byte(paddingLen - 1) + } + if config.Bugs.PaddingFirstByteBad || config.Bugs.PaddingFirstByteBadIf255 && paddingLen == 256 { + finalBlock[overrun] ^= 0xff + } + copy(finalBlock, payload[len(payload)-overrun:]) + return +} + +// encrypt encrypts and macs the data in b. +func (hc *halfConn) encrypt(b *block, explicitIVLen int, typ recordType) (bool, alert) { + recordHeaderLen := hc.recordHeaderLen() + + // mac + if hc.mac != nil { + mac := hc.mac.MAC(hc.outDigestBuf, hc.outSeq[0:], b.data[:3], b.data[recordHeaderLen-2:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:]) + + n := len(b.data) + b.resize(n + len(mac)) + copy(b.data[n:], mac) + hc.outDigestBuf = mac + } + + payload := b.data[recordHeaderLen:] + + // encrypt + if hc.cipher != nil { + // Add TLS 1.3 padding. + if hc.version >= VersionTLS13 { + paddingLen := hc.config.Bugs.RecordPadding + if hc.config.Bugs.OmitRecordContents { + b.resize(recordHeaderLen + paddingLen) + } else { + b.resize(len(b.data) + 1 + paddingLen) + b.data[len(b.data)-paddingLen-1] = byte(typ) + } + for i := 0; i < paddingLen; i++ { + b.data[len(b.data)-paddingLen+i] = 0 + } + } + + switch c := hc.cipher.(type) { + case cipher.Stream: + c.XORKeyStream(payload, payload) + case *tlsAead: + payloadLen := len(b.data) - recordHeaderLen - explicitIVLen + b.resize(len(b.data) + c.Overhead()) + nonce := hc.outSeq[:] + if c.explicitNonce { + nonce = b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + } + payload := b.data[recordHeaderLen+explicitIVLen:] + payload = payload[:payloadLen] + + var additionalData []byte + if hc.version < VersionTLS13 { + additionalData = make([]byte, 13) + copy(additionalData, hc.outSeq[:]) + copy(additionalData[8:], b.data[:3]) + additionalData[11] = byte(payloadLen >> 8) + additionalData[12] = byte(payloadLen) + } + + c.Seal(payload[:0], nonce, payload, additionalData) + case cbcMode: + blockSize := c.BlockSize() + if explicitIVLen > 0 { + c.SetIV(payload[:explicitIVLen]) + payload = payload[explicitIVLen:] + } + prefix, finalBlock := padToBlockSize(payload, blockSize, hc.config) + b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock)) + c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix) + c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock) + case nullCipher: + break + default: + panic("unknown cipher type") + } + } + + // update length to include MAC and any block padding needed. + n := len(b.data) - recordHeaderLen + b.data[recordHeaderLen-2] = byte(n >> 8) + b.data[recordHeaderLen-1] = byte(n) + hc.incSeq(true) + + return true, 0 +} + +// A block is a simple data buffer. +type block struct { + data []byte + off int // index for Read + link *block +} + +// resize resizes block to be n bytes, growing if necessary. +func (b *block) resize(n int) { + if n > cap(b.data) { + b.reserve(n) + } + b.data = b.data[0:n] +} + +// reserve makes sure that block contains a capacity of at least n bytes. +func (b *block) reserve(n int) { + if cap(b.data) >= n { + return + } + m := cap(b.data) + if m == 0 { + m = 1024 + } + for m < n { + m *= 2 + } + data := make([]byte, len(b.data), m) + copy(data, b.data) + b.data = data +} + +// readFromUntil reads from r into b until b contains at least n bytes +// or else returns an error. +func (b *block) readFromUntil(r io.Reader, n int) error { + // quick case + if len(b.data) >= n { + return nil + } + + // read until have enough. + b.reserve(n) + for { + m, err := r.Read(b.data[len(b.data):cap(b.data)]) + b.data = b.data[0 : len(b.data)+m] + if len(b.data) >= n { + // TODO(bradfitz,agl): slightly suspicious + // that we're throwing away r.Read's err here. + break + } + if err != nil { + return err + } + } + return nil +} + +func (b *block) Read(p []byte) (n int, err error) { + n = copy(p, b.data[b.off:]) + b.off += n + return +} + +// newBlock allocates a new block, from hc's free list if possible. +func (hc *halfConn) newBlock() *block { + b := hc.bfree + if b == nil { + return new(block) + } + hc.bfree = b.link + b.link = nil + b.resize(0) + return b +} + +// freeBlock returns a block to hc's free list. +// The protocol is such that each side only has a block or two on +// its free list at a time, so there's no need to worry about +// trimming the list, etc. +func (hc *halfConn) freeBlock(b *block) { + b.link = hc.bfree + hc.bfree = b +} + +// splitBlock splits a block after the first n bytes, +// returning a block with those n bytes and a +// block with the remainder. the latter may be nil. +func (hc *halfConn) splitBlock(b *block, n int) (*block, *block) { + if len(b.data) <= n { + return b, nil + } + bb := hc.newBlock() + bb.resize(len(b.data) - n) + copy(bb.data, b.data[n:]) + b.data = b.data[0:n] + return b, bb +} + +func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) { +RestartReadRecord: + if c.isDTLS { + return c.dtlsDoReadRecord(want) + } + + recordHeaderLen := c.in.recordHeaderLen() + + if c.rawInput == nil { + c.rawInput = c.in.newBlock() + } + b := c.rawInput + + // Read header, payload. + if err := b.readFromUntil(c.conn, recordHeaderLen); err != nil { + // RFC suggests that EOF without an alertCloseNotify is + // an error, but popular web sites seem to do this, + // so we can't make it an error, outside of tests. + if err == io.EOF && c.config.Bugs.ExpectCloseNotify { + err = io.ErrUnexpectedEOF + } + if e, ok := err.(net.Error); !ok || !e.Temporary() { + c.in.setErrorLocked(err) + } + return 0, nil, err + } + + typ := recordType(b.data[0]) + + // No valid TLS record has a type of 0x80, however SSLv2 handshakes + // start with a uint16 length where the MSB is set and the first record + // is always < 256 bytes long. Therefore typ == 0x80 strongly suggests + // an SSLv2 client. + if want == recordTypeHandshake && typ == 0x80 { + c.sendAlert(alertProtocolVersion) + return 0, nil, c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received")) + } + + vers := uint16(b.data[1])<<8 | uint16(b.data[2]) + n := int(b.data[3])<<8 | int(b.data[4]) + + // Alerts sent near version negotiation do not have a well-defined + // record-layer version prior to TLS 1.3. (In TLS 1.3, the record-layer + // version is irrelevant.) + if typ != recordTypeAlert { + var expect uint16 + if c.haveVers { + expect = c.vers + if c.vers >= VersionTLS13 { + expect = VersionTLS10 + } + } else { + expect = c.config.Bugs.ExpectInitialRecordVersion + } + if expect != 0 && vers != expect { + c.sendAlert(alertProtocolVersion) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, expect)) + } + } + if n > maxCiphertext { + c.sendAlert(alertRecordOverflow) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: oversized record received with length %d", n)) + } + if !c.haveVers { + // First message, be extra suspicious: + // this might not be a TLS client. + // Bail out before reading a full 'body', if possible. + // The current max version is 3.1. + // If the version is >= 16.0, it's probably not real. + // Similarly, a clientHello message encodes in + // well under a kilobyte. If the length is >= 12 kB, + // it's probably not real. + if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 { + c.sendAlert(alertUnexpectedMessage) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: first record does not look like a TLS handshake")) + } + } + if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + if e, ok := err.(net.Error); !ok || !e.Temporary() { + c.in.setErrorLocked(err) + } + return 0, nil, err + } + + // Process message. + b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) + ok, off, encTyp, alertValue := c.in.decrypt(b) + + // Handle skipping over early data. + if !ok && c.skipEarlyData { + goto RestartReadRecord + } + + // If the server is expecting a second ClientHello (in response to + // a HelloRetryRequest) and the client sends early data, there + // won't be a decryption failure but it still needs to be skipped. + if c.in.cipher == nil && typ == recordTypeApplicationData && c.skipEarlyData { + goto RestartReadRecord + } + + if !ok { + return 0, nil, c.in.setErrorLocked(c.sendAlert(alertValue)) + } + b.off = off + c.skipEarlyData = false + + if c.vers >= VersionTLS13 && c.in.cipher != nil { + if typ != recordTypeApplicationData { + return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: outer record type is not application data")) + } + typ = encTyp + } + return typ, b, nil +} + +// readRecord reads the next TLS record from the connection +// and updates the record layer state. +// c.in.Mutex <= L; c.input == nil. +func (c *Conn) readRecord(want recordType) error { + // Caller must be in sync with connection: + // handshake data if handshake not yet completed, + // else application data. + switch want { + default: + c.sendAlert(alertInternalError) + return c.in.setErrorLocked(errors.New("tls: unknown record type requested")) + case recordTypeHandshake, recordTypeChangeCipherSpec: + if c.handshakeComplete { + c.sendAlert(alertInternalError) + return c.in.setErrorLocked(errors.New("tls: handshake or ChangeCipherSpec requested after handshake complete")) + } + case recordTypeApplicationData: + if !c.handshakeComplete && !c.config.Bugs.ExpectFalseStart && len(c.config.Bugs.ExpectHalfRTTData) == 0 && len(c.config.Bugs.ExpectEarlyData) == 0 { + c.sendAlert(alertInternalError) + return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete")) + } + case recordTypeAlert: + // Looking for a close_notify. Note: unlike a real + // implementation, this is not tolerant of additional records. + // See the documentation for ExpectCloseNotify. + } + +Again: + typ, b, err := c.doReadRecord(want) + if err != nil { + return err + } + data := b.data[b.off:] + max := maxPlaintext + if c.config.Bugs.MaxReceivePlaintext != 0 { + max = c.config.Bugs.MaxReceivePlaintext + } + if len(data) > max { + err := c.sendAlert(alertRecordOverflow) + c.in.freeBlock(b) + return c.in.setErrorLocked(err) + } + + switch typ { + default: + c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + + case recordTypeAlert: + if len(data) != 2 { + c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + break + } + if alert(data[1]) == alertCloseNotify { + c.in.setErrorLocked(io.EOF) + break + } + switch data[0] { + case alertLevelWarning: + if alert(data[1]) == alertNoCertificate { + c.in.freeBlock(b) + return errNoCertificateAlert + } + if alert(data[1]) == alertEndOfEarlyData { + c.in.freeBlock(b) + return errEndOfEarlyDataAlert + } + + // drop on the floor + c.in.freeBlock(b) + goto Again + case alertLevelError: + c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])}) + default: + c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + + case recordTypeChangeCipherSpec: + if typ != want || len(data) != 1 || data[0] != 1 { + c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + break + } + err := c.in.changeCipherSpec(c.config) + if err != nil { + c.in.setErrorLocked(c.sendAlert(err.(alert))) + } + + case recordTypeApplicationData: + if typ != want { + c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + break + } + c.input = b + b = nil + + case recordTypeHandshake: + // Allow handshake data while reading application data to + // trigger post-handshake messages. + // TODO(rsc): Should at least pick off connection close. + if typ != want && want != recordTypeApplicationData { + return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation)) + } + c.hand.Write(data) + } + + if b != nil { + c.in.freeBlock(b) + } + return c.in.err +} + +// sendAlert sends a TLS alert message. +// c.out.Mutex <= L. +func (c *Conn) sendAlertLocked(level byte, err alert) error { + c.tmp[0] = level + c.tmp[1] = byte(err) + if c.config.Bugs.FragmentAlert { + c.writeRecord(recordTypeAlert, c.tmp[0:1]) + c.writeRecord(recordTypeAlert, c.tmp[1:2]) + } else if c.config.Bugs.DoubleAlert { + copy(c.tmp[2:4], c.tmp[0:2]) + c.writeRecord(recordTypeAlert, c.tmp[0:4]) + } else { + c.writeRecord(recordTypeAlert, c.tmp[0:2]) + } + // Error alerts are fatal to the connection. + if level == alertLevelError { + return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err}) + } + return nil +} + +// sendAlert sends a TLS alert message. +// L < c.out.Mutex. +func (c *Conn) sendAlert(err alert) error { + level := byte(alertLevelError) + if err == alertNoRenegotiation || err == alertCloseNotify || err == alertNoCertificate || err == alertEndOfEarlyData { + level = alertLevelWarning + } + return c.SendAlert(level, err) +} + +func (c *Conn) SendAlert(level byte, err alert) error { + c.out.Lock() + defer c.out.Unlock() + return c.sendAlertLocked(level, err) +} + +// writeV2Record writes a record for a V2ClientHello. +func (c *Conn) writeV2Record(data []byte) (n int, err error) { + record := make([]byte, 2+len(data)) + record[0] = uint8(len(data)>>8) | 0x80 + record[1] = uint8(len(data)) + copy(record[2:], data) + return c.conn.Write(record) +} + +// writeRecord writes a TLS record with the given type and payload +// to the connection and updates the record layer state. +// c.out.Mutex <= L. +func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { + if msgType := c.config.Bugs.SendWrongMessageType; msgType != 0 { + if typ == recordTypeHandshake && data[0] == msgType { + newData := make([]byte, len(data)) + copy(newData, data) + newData[0] += 42 + data = newData + } + } + + if msgType := c.config.Bugs.SendTrailingMessageData; msgType != 0 { + if typ == recordTypeHandshake && data[0] == msgType { + newData := make([]byte, len(data)) + copy(newData, data) + + // Add a 0 to the body. + newData = append(newData, 0) + // Fix the header. + newLen := len(newData) - 4 + newData[1] = byte(newLen >> 16) + newData[2] = byte(newLen >> 8) + newData[3] = byte(newLen) + + data = newData + } + } + + if c.isDTLS { + return c.dtlsWriteRecord(typ, data) + } + + if typ == recordTypeHandshake { + if c.config.Bugs.SendHelloRequestBeforeEveryHandshakeMessage { + newData := make([]byte, 0, 4+len(data)) + newData = append(newData, typeHelloRequest, 0, 0, 0) + newData = append(newData, data...) + data = newData + } + + if c.config.Bugs.PackHandshakeFlight { + c.pendingFlight.Write(data) + return len(data), nil + } + } + + return c.doWriteRecord(typ, data) +} + +func (c *Conn) doWriteRecord(typ recordType, data []byte) (n int, err error) { + recordHeaderLen := c.out.recordHeaderLen() + b := c.out.newBlock() + first := true + isClientHello := typ == recordTypeHandshake && len(data) > 0 && data[0] == typeClientHello + for len(data) > 0 || first { + m := len(data) + if m > maxPlaintext && !c.config.Bugs.SendLargeRecords { + m = maxPlaintext + } + if typ == recordTypeHandshake && c.config.Bugs.MaxHandshakeRecordLength > 0 && m > c.config.Bugs.MaxHandshakeRecordLength { + m = c.config.Bugs.MaxHandshakeRecordLength + // By default, do not fragment the client_version or + // server_version, which are located in the first 6 + // bytes. + if first && isClientHello && !c.config.Bugs.FragmentClientVersion && m < 6 { + m = 6 + } + } + explicitIVLen := 0 + explicitIVIsSeq := false + first = false + + var cbc cbcMode + if c.out.version >= VersionTLS11 { + var ok bool + if cbc, ok = c.out.cipher.(cbcMode); ok { + explicitIVLen = cbc.BlockSize() + } + } + if explicitIVLen == 0 { + if aead, ok := c.out.cipher.(*tlsAead); ok && aead.explicitNonce { + explicitIVLen = 8 + // The AES-GCM construction in TLS has an + // explicit nonce so that the nonce can be + // random. However, the nonce is only 8 bytes + // which is too small for a secure, random + // nonce. Therefore we use the sequence number + // as the nonce. + explicitIVIsSeq = true + } + } + b.resize(recordHeaderLen + explicitIVLen + m) + b.data[0] = byte(typ) + if c.vers >= VersionTLS13 && c.out.cipher != nil { + b.data[0] = byte(recordTypeApplicationData) + if outerType := c.config.Bugs.OuterRecordType; outerType != 0 { + b.data[0] = byte(outerType) + } + } + vers := c.vers + if vers == 0 || vers >= VersionTLS13 { + // Some TLS servers fail if the record version is + // greater than TLS 1.0 for the initial ClientHello. + // + // TLS 1.3 fixes the version number in the record + // layer to {3, 1}. + vers = VersionTLS10 + } + if c.config.Bugs.SendRecordVersion != 0 { + vers = c.config.Bugs.SendRecordVersion + } + if c.vers == 0 && c.config.Bugs.SendInitialRecordVersion != 0 { + vers = c.config.Bugs.SendInitialRecordVersion + } + b.data[1] = byte(vers >> 8) + b.data[2] = byte(vers) + b.data[3] = byte(m >> 8) + b.data[4] = byte(m) + if explicitIVLen > 0 { + explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + if explicitIVIsSeq { + copy(explicitIV, c.out.seq[:]) + } else { + if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { + break + } + } + } + copy(b.data[recordHeaderLen+explicitIVLen:], data) + c.out.encrypt(b, explicitIVLen, typ) + _, err = c.conn.Write(b.data) + if err != nil { + break + } + n += m + data = data[m:] + } + c.out.freeBlock(b) + + if typ == recordTypeChangeCipherSpec { + err = c.out.changeCipherSpec(c.config) + if err != nil { + // Cannot call sendAlert directly, + // because we already hold c.out.Mutex. + c.tmp[0] = alertLevelError + c.tmp[1] = byte(err.(alert)) + c.writeRecord(recordTypeAlert, c.tmp[0:2]) + return n, c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err}) + } + } + return +} + +func (c *Conn) flushHandshake() error { + if c.isDTLS { + return c.dtlsFlushHandshake() + } + + for c.pendingFlight.Len() > 0 { + var buf [maxPlaintext]byte + n, _ := c.pendingFlight.Read(buf[:]) + if _, err := c.doWriteRecord(recordTypeHandshake, buf[:n]); err != nil { + return err + } + } + + c.pendingFlight.Reset() + return nil +} + +func (c *Conn) doReadHandshake() ([]byte, error) { + if c.isDTLS { + return c.dtlsDoReadHandshake() + } + + for c.hand.Len() < 4 { + if err := c.in.err; err != nil { + return nil, err + } + if err := c.readRecord(recordTypeHandshake); err != nil { + return nil, err + } + } + + data := c.hand.Bytes() + n := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + if n > maxHandshake { + return nil, c.in.setErrorLocked(c.sendAlert(alertInternalError)) + } + for c.hand.Len() < 4+n { + if err := c.in.err; err != nil { + return nil, err + } + if err := c.readRecord(recordTypeHandshake); err != nil { + return nil, err + } + } + return c.hand.Next(4 + n), nil +} + +// readHandshake reads the next handshake message from +// the record layer. +// c.in.Mutex < L; c.out.Mutex < L. +func (c *Conn) readHandshake() (interface{}, error) { + data, err := c.doReadHandshake() + if err == errNoCertificateAlert { + if c.hand.Len() != 0 { + // The warning alert may not interleave with a handshake message. + return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + return new(ssl3NoCertificateMsg), nil + } + if err != nil { + return nil, err + } + + var m handshakeMessage + switch data[0] { + case typeHelloRequest: + m = new(helloRequestMsg) + case typeClientHello: + m = &clientHelloMsg{ + isDTLS: c.isDTLS, + } + case typeServerHello: + m = &serverHelloMsg{ + isDTLS: c.isDTLS, + } + case typeHelloRetryRequest: + m = new(helloRetryRequestMsg) + case typeNewSessionTicket: + m = &newSessionTicketMsg{ + version: c.vers, + } + case typeEncryptedExtensions: + m = new(encryptedExtensionsMsg) + case typeCertificate: + m = &certificateMsg{ + hasRequestContext: c.vers >= VersionTLS13, + } + case typeCertificateRequest: + m = &certificateRequestMsg{ + hasSignatureAlgorithm: c.vers >= VersionTLS12, + hasRequestContext: c.vers >= VersionTLS13, + } + case typeCertificateStatus: + m = new(certificateStatusMsg) + case typeServerKeyExchange: + m = new(serverKeyExchangeMsg) + case typeServerHelloDone: + m = new(serverHelloDoneMsg) + case typeClientKeyExchange: + m = new(clientKeyExchangeMsg) + case typeCertificateVerify: + m = &certificateVerifyMsg{ + hasSignatureAlgorithm: c.vers >= VersionTLS12, + } + case typeNextProtocol: + m = new(nextProtoMsg) + case typeFinished: + m = new(finishedMsg) + case typeHelloVerifyRequest: + m = new(helloVerifyRequestMsg) + case typeChannelID: + m = new(channelIDMsg) + case typeKeyUpdate: + m = new(keyUpdateMsg) + default: + return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + + // The handshake message unmarshallers + // expect to be able to keep references to data, + // so pass in a fresh copy that won't be overwritten. + data = append([]byte(nil), data...) + + if !m.unmarshal(data) { + return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage)) + } + return m, nil +} + +// skipPacket processes all the DTLS records in packet. It updates +// sequence number expectations but otherwise ignores them. +func (c *Conn) skipPacket(packet []byte) error { + for len(packet) > 0 { + if len(packet) < 13 { + return errors.New("tls: bad packet") + } + // Dropped packets are completely ignored save to update + // expected sequence numbers for this and the next epoch. (We + // don't assert on the contents of the packets both for + // simplicity and because a previous test with one shorter + // timeout schedule would have done so.) + epoch := packet[3:5] + seq := packet[5:11] + length := uint16(packet[11])<<8 | uint16(packet[12]) + if bytes.Equal(c.in.seq[:2], epoch) { + if bytes.Compare(seq, c.in.seq[2:]) < 0 { + return errors.New("tls: sequence mismatch") + } + copy(c.in.seq[2:], seq) + c.in.incSeq(false) + } else { + if bytes.Compare(seq, c.in.nextSeq[:]) < 0 { + return errors.New("tls: sequence mismatch") + } + copy(c.in.nextSeq[:], seq) + c.in.incNextSeq() + } + if len(packet) < 13+int(length) { + return errors.New("tls: bad packet") + } + packet = packet[13+length:] + } + return nil +} + +// simulatePacketLoss simulates the loss of a handshake leg from the +// peer based on the schedule in c.config.Bugs. If resendFunc is +// non-nil, it is called after each simulated timeout to retransmit +// handshake messages from the local end. This is used in cases where +// the peer retransmits on a stale Finished rather than a timeout. +func (c *Conn) simulatePacketLoss(resendFunc func()) error { + if len(c.config.Bugs.TimeoutSchedule) == 0 { + return nil + } + if !c.isDTLS { + return errors.New("tls: TimeoutSchedule may only be set in DTLS") + } + if c.config.Bugs.PacketAdaptor == nil { + return errors.New("tls: TimeoutSchedule set without PacketAdapter") + } + for _, timeout := range c.config.Bugs.TimeoutSchedule { + // Simulate a timeout. + packets, err := c.config.Bugs.PacketAdaptor.SendReadTimeout(timeout) + if err != nil { + return err + } + for _, packet := range packets { + if err := c.skipPacket(packet); err != nil { + return err + } + } + if resendFunc != nil { + resendFunc() + } + } + return nil +} + +func (c *Conn) SendHalfHelloRequest() error { + if err := c.Handshake(); err != nil { + return err + } + + c.out.Lock() + defer c.out.Unlock() + + if _, err := c.writeRecord(recordTypeHandshake, []byte{typeHelloRequest, 0}); err != nil { + return err + } + return c.flushHandshake() +} + +// Write writes data to the connection. +func (c *Conn) Write(b []byte) (int, error) { + if err := c.Handshake(); err != nil { + return 0, err + } + + c.out.Lock() + defer c.out.Unlock() + + // Flush any pending handshake data. PackHelloRequestWithFinished may + // have been set and the handshake not followed by Renegotiate. + c.flushHandshake() + + if err := c.out.err; err != nil { + return 0, err + } + + if !c.handshakeComplete { + return 0, alertInternalError + } + + if c.keyUpdateRequested { + if err := c.sendKeyUpdateLocked(keyUpdateNotRequested); err != nil { + return 0, err + } + c.keyUpdateRequested = false + } + + if c.config.Bugs.SendSpuriousAlert != 0 { + c.sendAlertLocked(alertLevelError, c.config.Bugs.SendSpuriousAlert) + } + + if c.config.Bugs.SendHelloRequestBeforeEveryAppDataRecord { + c.writeRecord(recordTypeHandshake, []byte{typeHelloRequest, 0, 0, 0}) + c.flushHandshake() + } + + // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext + // attack when using block mode ciphers due to predictable IVs. + // This can be prevented by splitting each Application Data + // record into two records, effectively randomizing the IV. + // + // http://www.openssl.org/~bodo/tls-cbc.txt + // https://bugzilla.mozilla.org/show_bug.cgi?id=665814 + // http://www.imperialviolet.org/2012/01/15/beastfollowup.html + + var m int + if len(b) > 1 && c.vers <= VersionTLS10 && !c.isDTLS { + if _, ok := c.out.cipher.(cipher.BlockMode); ok { + n, err := c.writeRecord(recordTypeApplicationData, b[:1]) + if err != nil { + return n, c.out.setErrorLocked(err) + } + m, b = 1, b[1:] + } + } + + n, err := c.writeRecord(recordTypeApplicationData, b) + return n + m, c.out.setErrorLocked(err) +} + +func (c *Conn) handlePostHandshakeMessage() error { + msg, err := c.readHandshake() + if err != nil { + return err + } + + if c.vers < VersionTLS13 { + if !c.isClient { + c.sendAlert(alertUnexpectedMessage) + return errors.New("tls: unexpected post-handshake message") + } + + _, ok := msg.(*helloRequestMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return alertUnexpectedMessage + } + + c.handshakeComplete = false + return c.Handshake() + } + + if c.isClient { + if newSessionTicket, ok := msg.(*newSessionTicketMsg); ok { + if c.config.Bugs.ExpectGREASE && !newSessionTicket.hasGREASEExtension { + return errors.New("tls: no GREASE ticket extension found") + } + + if c.config.Bugs.ExpectTicketEarlyDataInfo && newSessionTicket.maxEarlyDataSize == 0 { + return errors.New("tls: no ticket_early_data_info extension found") + } + + if c.config.Bugs.ExpectNoNewSessionTicket { + return errors.New("tls: received unexpected NewSessionTicket") + } + + if c.config.ClientSessionCache == nil || newSessionTicket.ticketLifetime == 0 { + return nil + } + + session := &ClientSessionState{ + sessionTicket: newSessionTicket.ticket, + vers: c.vers, + cipherSuite: c.cipherSuite.id, + masterSecret: c.resumptionSecret, + serverCertificates: c.peerCertificates, + sctList: c.sctList, + ocspResponse: c.ocspResponse, + ticketCreationTime: c.config.time(), + ticketExpiration: c.config.time().Add(time.Duration(newSessionTicket.ticketLifetime) * time.Second), + ticketAgeAdd: newSessionTicket.ticketAgeAdd, + maxEarlyDataSize: newSessionTicket.maxEarlyDataSize, + } + + cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + c.config.ClientSessionCache.Put(cacheKey, session) + return nil + } + } + + if keyUpdate, ok := msg.(*keyUpdateMsg); ok { + c.in.doKeyUpdate(c, false) + if keyUpdate.keyUpdateRequest == keyUpdateRequested { + c.keyUpdateRequested = true + } + return nil + } + + // TODO(davidben): Add support for KeyUpdate. + c.sendAlert(alertUnexpectedMessage) + return alertUnexpectedMessage +} + +func (c *Conn) Renegotiate() error { + if !c.isClient { + helloReq := new(helloRequestMsg).marshal() + if c.config.Bugs.BadHelloRequest != nil { + helloReq = c.config.Bugs.BadHelloRequest + } + c.writeRecord(recordTypeHandshake, helloReq) + c.flushHandshake() + } + + c.handshakeComplete = false + return c.Handshake() +} + +// Read can be made to time out and return a net.Error with Timeout() == true +// after a fixed time limit; see SetDeadline and SetReadDeadline. +func (c *Conn) Read(b []byte) (n int, err error) { + if err = c.Handshake(); err != nil { + return + } + + c.in.Lock() + defer c.in.Unlock() + + // Some OpenSSL servers send empty records in order to randomize the + // CBC IV. So this loop ignores a limited number of empty records. + const maxConsecutiveEmptyRecords = 100 + for emptyRecordCount := 0; emptyRecordCount <= maxConsecutiveEmptyRecords; emptyRecordCount++ { + for c.input == nil && c.in.err == nil { + if err := c.readRecord(recordTypeApplicationData); err != nil { + // Soft error, like EAGAIN + return 0, err + } + if c.hand.Len() > 0 { + // We received handshake bytes, indicating a + // post-handshake message. + if err := c.handlePostHandshakeMessage(); err != nil { + return 0, err + } + continue + } + } + if err := c.in.err; err != nil { + return 0, err + } + + n, err = c.input.Read(b) + if c.input.off >= len(c.input.data) || c.isDTLS { + c.in.freeBlock(c.input) + c.input = nil + } + + // If a close-notify alert is waiting, read it so that + // we can return (n, EOF) instead of (n, nil), to signal + // to the HTTP response reading goroutine that the + // connection is now closed. This eliminates a race + // where the HTTP response reading goroutine would + // otherwise not observe the EOF until its next read, + // by which time a client goroutine might have already + // tried to reuse the HTTP connection for a new + // request. + // See https://codereview.appspot.com/76400046 + // and http://golang.org/issue/3514 + if ri := c.rawInput; ri != nil && + n != 0 && err == nil && + c.input == nil && len(ri.data) > 0 && recordType(ri.data[0]) == recordTypeAlert { + if recErr := c.readRecord(recordTypeApplicationData); recErr != nil { + err = recErr // will be io.EOF on closeNotify + } + } + + if n != 0 || err != nil { + return n, err + } + } + + return 0, io.ErrNoProgress +} + +// Close closes the connection. +func (c *Conn) Close() error { + var alertErr error + + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if c.handshakeComplete && !c.config.Bugs.NoCloseNotify { + alert := alertCloseNotify + if c.config.Bugs.SendAlertOnShutdown != 0 { + alert = c.config.Bugs.SendAlertOnShutdown + } + alertErr = c.sendAlert(alert) + // Clear local alerts when sending alerts so we continue to wait + // for the peer rather than closing the socket early. + if opErr, ok := alertErr.(*net.OpError); ok && opErr.Op == "local error" { + alertErr = nil + } + } + + // Consume a close_notify from the peer if one hasn't been received + // already. This avoids the peer from failing |SSL_shutdown| due to a + // write failing. + if c.handshakeComplete && alertErr == nil && c.config.Bugs.ExpectCloseNotify { + for c.in.error() == nil { + c.readRecord(recordTypeAlert) + } + if c.in.error() != io.EOF { + alertErr = c.in.error() + } + } + + if err := c.conn.Close(); err != nil { + return err + } + return alertErr +} + +// Handshake runs the client or server handshake +// protocol if it has not yet been run. +// Most uses of this package need not call Handshake +// explicitly: the first Read or Write will call it automatically. +func (c *Conn) Handshake() error { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if err := c.handshakeErr; err != nil { + return err + } + if c.handshakeComplete { + return nil + } + + if c.isDTLS && c.config.Bugs.SendSplitAlert { + c.conn.Write([]byte{ + byte(recordTypeAlert), // type + 0xfe, 0xff, // version + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // sequence + 0x0, 0x2, // length + }) + c.conn.Write([]byte{alertLevelError, byte(alertInternalError)}) + } + if data := c.config.Bugs.AppDataBeforeHandshake; data != nil { + c.writeRecord(recordTypeApplicationData, data) + } + if c.isClient { + c.handshakeErr = c.clientHandshake() + } else { + c.handshakeErr = c.serverHandshake() + } + if c.handshakeErr == nil && c.config.Bugs.SendInvalidRecordType { + c.writeRecord(recordType(42), []byte("invalid record")) + } + return c.handshakeErr +} + +// ConnectionState returns basic TLS details about the connection. +func (c *Conn) ConnectionState() ConnectionState { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + + var state ConnectionState + state.HandshakeComplete = c.handshakeComplete + if c.handshakeComplete { + state.Version = c.vers + state.NegotiatedProtocol = c.clientProtocol + state.DidResume = c.didResume + state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback + state.NegotiatedProtocolFromALPN = c.usedALPN + state.CipherSuite = c.cipherSuite.id + state.PeerCertificates = c.peerCertificates + state.VerifiedChains = c.verifiedChains + state.ServerName = c.serverName + state.ChannelID = c.channelID + state.SRTPProtectionProfile = c.srtpProtectionProfile + state.TLSUnique = c.firstFinished[:] + state.SCTList = c.sctList + state.PeerSignatureAlgorithm = c.peerSignatureAlgorithm + state.CurveID = c.curveID + } + + return state +} + +// OCSPResponse returns the stapled OCSP response from the TLS server, if +// any. (Only valid for client connections.) +func (c *Conn) OCSPResponse() []byte { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + + return c.ocspResponse +} + +// VerifyHostname checks that the peer certificate chain is valid for +// connecting to host. If so, it returns nil; if not, it returns an error +// describing the problem. +func (c *Conn) VerifyHostname(host string) error { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if !c.isClient { + return errors.New("tls: VerifyHostname called on TLS server connection") + } + if !c.handshakeComplete { + return errors.New("tls: handshake has not yet been performed") + } + return c.peerCertificates[0].VerifyHostname(host) +} + +// ExportKeyingMaterial exports keying material from the current connection +// state, as per RFC 5705. +func (c *Conn) ExportKeyingMaterial(length int, label, context []byte, useContext bool) ([]byte, error) { + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + if !c.handshakeComplete { + return nil, errors.New("tls: handshake has not yet been performed") + } + + if c.vers >= VersionTLS13 { + // TODO(davidben): What should we do with useContext? See + // https://github.com/tlswg/tls13-spec/issues/546 + return hkdfExpandLabel(c.cipherSuite.hash(), c.exporterSecret, label, context, length), nil + } + + seedLen := len(c.clientRandom) + len(c.serverRandom) + if useContext { + seedLen += 2 + len(context) + } + seed := make([]byte, 0, seedLen) + seed = append(seed, c.clientRandom[:]...) + seed = append(seed, c.serverRandom[:]...) + if useContext { + seed = append(seed, byte(len(context)>>8), byte(len(context))) + seed = append(seed, context...) + } + result := make([]byte, length) + prfForVersion(c.vers, c.cipherSuite)(result, c.exporterSecret, label, seed) + return result, nil +} + +// noRenegotiationInfo returns true if the renegotiation info extension +// should be supported in the current handshake. +func (c *Conn) noRenegotiationInfo() bool { + if c.config.Bugs.NoRenegotiationInfo { + return true + } + if c.cipherSuite == nil && c.config.Bugs.NoRenegotiationInfoInInitial { + return true + } + if c.cipherSuite != nil && c.config.Bugs.NoRenegotiationInfoAfterInitial { + return true + } + return false +} + +func (c *Conn) SendNewSessionTicket() error { + if c.isClient || c.vers < VersionTLS13 { + return errors.New("tls: cannot send post-handshake NewSessionTicket") + } + + var peerCertificatesRaw [][]byte + for _, cert := range c.peerCertificates { + peerCertificatesRaw = append(peerCertificatesRaw, cert.Raw) + } + + addBuffer := make([]byte, 4) + _, err := io.ReadFull(c.config.rand(), addBuffer) + if err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: short read from Rand: " + err.Error()) + } + ticketAgeAdd := uint32(addBuffer[3])<<24 | uint32(addBuffer[2])<<16 | uint32(addBuffer[1])<<8 | uint32(addBuffer[0]) + + // TODO(davidben): Allow configuring these values. + m := &newSessionTicketMsg{ + version: c.vers, + ticketLifetime: uint32(24 * time.Hour / time.Second), + duplicateEarlyDataInfo: c.config.Bugs.DuplicateTicketEarlyDataInfo, + customExtension: c.config.Bugs.CustomTicketExtension, + ticketAgeAdd: ticketAgeAdd, + maxEarlyDataSize: c.config.MaxEarlyDataSize, + } + + if c.config.Bugs.SendTicketLifetime != 0 { + m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second) + } + + state := sessionState{ + vers: c.vers, + cipherSuite: c.cipherSuite.id, + masterSecret: c.resumptionSecret, + certificates: peerCertificatesRaw, + ticketCreationTime: c.config.time(), + ticketExpiration: c.config.time().Add(time.Duration(m.ticketLifetime) * time.Second), + ticketAgeAdd: uint32(addBuffer[3])<<24 | uint32(addBuffer[2])<<16 | uint32(addBuffer[1])<<8 | uint32(addBuffer[0]), + } + + if !c.config.Bugs.SendEmptySessionTicket { + var err error + m.ticket, err = c.encryptTicket(&state) + if err != nil { + return err + } + } + + c.out.Lock() + defer c.out.Unlock() + _, err = c.writeRecord(recordTypeHandshake, m.marshal()) + return err +} + +func (c *Conn) SendKeyUpdate(keyUpdateRequest byte) error { + c.out.Lock() + defer c.out.Unlock() + return c.sendKeyUpdateLocked(keyUpdateRequest) +} + +func (c *Conn) sendKeyUpdateLocked(keyUpdateRequest byte) error { + if c.vers < VersionTLS13 { + return errors.New("tls: attempted to send KeyUpdate before TLS 1.3") + } + + m := keyUpdateMsg{ + keyUpdateRequest: keyUpdateRequest, + } + if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil { + return err + } + if err := c.flushHandshake(); err != nil { + return err + } + c.out.doKeyUpdate(c, true) + return nil +} + +func (c *Conn) sendFakeEarlyData(len int) error { + // Assemble a fake early data record. This does not use writeRecord + // because the record layer may be using different keys at this point. + payload := make([]byte, 5+len) + payload[0] = byte(recordTypeApplicationData) + payload[1] = 3 + payload[2] = 1 + payload[3] = byte(len >> 8) + payload[4] = byte(len) + _, err := c.conn.Write(payload) + return err +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/curve25519.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/curve25519.go new file mode 100644 index 000000000..6918c47fc --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/curve25519.go @@ -0,0 +1,841 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// We have a implementation in amd64 assembly so this code is only run on +// non-amd64 platforms. The amd64 assembly does not support gccgo. +// +build !amd64 gccgo appengine + +package curve25519 + +// This code is a port of the public domain, "ref10" implementation of +// curve25519 from SUPERCOP 20130419 by D. J. Bernstein. + +// fieldElement represents an element of the field GF(2^255 - 19). An element +// t, entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77 +// t[3]+2^102 t[4]+...+2^230 t[9]. Bounds on each t[i] vary depending on +// context. +type fieldElement [10]int32 + +func feZero(fe *fieldElement) { + for i := range fe { + fe[i] = 0 + } +} + +func feOne(fe *fieldElement) { + feZero(fe) + fe[0] = 1 +} + +func feAdd(dst, a, b *fieldElement) { + for i := range dst { + dst[i] = a[i] + b[i] + } +} + +func feSub(dst, a, b *fieldElement) { + for i := range dst { + dst[i] = a[i] - b[i] + } +} + +func feCopy(dst, src *fieldElement) { + for i := range dst { + dst[i] = src[i] + } +} + +// feCSwap replaces (f,g) with (g,f) if b == 1; replaces (f,g) with (f,g) if b == 0. +// +// Preconditions: b in {0,1}. +func feCSwap(f, g *fieldElement, b int32) { + var x fieldElement + b = -b + for i := range x { + x[i] = b & (f[i] ^ g[i]) + } + + for i := range f { + f[i] ^= x[i] + } + for i := range g { + g[i] ^= x[i] + } +} + +// load3 reads a 24-bit, little-endian value from in. +func load3(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + return r +} + +// load4 reads a 32-bit, little-endian value from in. +func load4(in []byte) int64 { + var r int64 + r = int64(in[0]) + r |= int64(in[1]) << 8 + r |= int64(in[2]) << 16 + r |= int64(in[3]) << 24 + return r +} + +func feFromBytes(dst *fieldElement, src *[32]byte) { + h0 := load4(src[:]) + h1 := load3(src[4:]) << 6 + h2 := load3(src[7:]) << 5 + h3 := load3(src[10:]) << 3 + h4 := load3(src[13:]) << 2 + h5 := load4(src[16:]) + h6 := load3(src[20:]) << 7 + h7 := load3(src[23:]) << 5 + h8 := load3(src[26:]) << 4 + h9 := load3(src[29:]) << 2 + + var carry [10]int64 + carry[9] = (h9 + 1<<24) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + carry[1] = (h1 + 1<<24) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[3] = (h3 + 1<<24) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[5] = (h5 + 1<<24) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + carry[7] = (h7 + 1<<24) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[0] = (h0 + 1<<25) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[2] = (h2 + 1<<25) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[4] = (h4 + 1<<25) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[6] = (h6 + 1<<25) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + carry[8] = (h8 + 1<<25) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + dst[0] = int32(h0) + dst[1] = int32(h1) + dst[2] = int32(h2) + dst[3] = int32(h3) + dst[4] = int32(h4) + dst[5] = int32(h5) + dst[6] = int32(h6) + dst[7] = int32(h7) + dst[8] = int32(h8) + dst[9] = int32(h9) +} + +// feToBytes marshals h to s. +// Preconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Write p=2^255-19; q=floor(h/p). +// Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). +// +// Proof: +// Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. +// Also have |h-2^230 h9|<2^230 so |19 2^(-255)(h-2^230 h9)|<1/4. +// +// Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). +// Then 0<y<1. +// +// Write r=h-pq. +// Have 0<=r<=p-1=2^255-20. +// Thus 0<=r+19(2^-255)r<r+19(2^-255)2^255<=2^255-1. +// +// Write x=r+19(2^-255)r+y. +// Then 0<x<2^255 so floor(2^(-255)x) = 0 so floor(q+2^(-255)x) = q. +// +// Have q+2^(-255)x = 2^(-255)(h + 19 2^(-25) h9 + 2^(-1)) +// so floor(2^(-255)(h + 19 2^(-25) h9 + 2^(-1))) = q. +func feToBytes(s *[32]byte, h *fieldElement) { + var carry [10]int32 + + q := (19*h[9] + (1 << 24)) >> 25 + q = (h[0] + q) >> 26 + q = (h[1] + q) >> 25 + q = (h[2] + q) >> 26 + q = (h[3] + q) >> 25 + q = (h[4] + q) >> 26 + q = (h[5] + q) >> 25 + q = (h[6] + q) >> 26 + q = (h[7] + q) >> 25 + q = (h[8] + q) >> 26 + q = (h[9] + q) >> 25 + + // Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. + h[0] += 19 * q + // Goal: Output h-2^255 q, which is between 0 and 2^255-20. + + carry[0] = h[0] >> 26 + h[1] += carry[0] + h[0] -= carry[0] << 26 + carry[1] = h[1] >> 25 + h[2] += carry[1] + h[1] -= carry[1] << 25 + carry[2] = h[2] >> 26 + h[3] += carry[2] + h[2] -= carry[2] << 26 + carry[3] = h[3] >> 25 + h[4] += carry[3] + h[3] -= carry[3] << 25 + carry[4] = h[4] >> 26 + h[5] += carry[4] + h[4] -= carry[4] << 26 + carry[5] = h[5] >> 25 + h[6] += carry[5] + h[5] -= carry[5] << 25 + carry[6] = h[6] >> 26 + h[7] += carry[6] + h[6] -= carry[6] << 26 + carry[7] = h[7] >> 25 + h[8] += carry[7] + h[7] -= carry[7] << 25 + carry[8] = h[8] >> 26 + h[9] += carry[8] + h[8] -= carry[8] << 26 + carry[9] = h[9] >> 25 + h[9] -= carry[9] << 25 + // h10 = carry9 + + // Goal: Output h[0]+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + // Have h[0]+...+2^230 h[9] between 0 and 2^255-1; + // evidently 2^255 h10-2^255 q = 0. + // Goal: Output h[0]+...+2^230 h[9]. + + s[0] = byte(h[0] >> 0) + s[1] = byte(h[0] >> 8) + s[2] = byte(h[0] >> 16) + s[3] = byte((h[0] >> 24) | (h[1] << 2)) + s[4] = byte(h[1] >> 6) + s[5] = byte(h[1] >> 14) + s[6] = byte((h[1] >> 22) | (h[2] << 3)) + s[7] = byte(h[2] >> 5) + s[8] = byte(h[2] >> 13) + s[9] = byte((h[2] >> 21) | (h[3] << 5)) + s[10] = byte(h[3] >> 3) + s[11] = byte(h[3] >> 11) + s[12] = byte((h[3] >> 19) | (h[4] << 6)) + s[13] = byte(h[4] >> 2) + s[14] = byte(h[4] >> 10) + s[15] = byte(h[4] >> 18) + s[16] = byte(h[5] >> 0) + s[17] = byte(h[5] >> 8) + s[18] = byte(h[5] >> 16) + s[19] = byte((h[5] >> 24) | (h[6] << 1)) + s[20] = byte(h[6] >> 7) + s[21] = byte(h[6] >> 15) + s[22] = byte((h[6] >> 23) | (h[7] << 3)) + s[23] = byte(h[7] >> 5) + s[24] = byte(h[7] >> 13) + s[25] = byte((h[7] >> 21) | (h[8] << 4)) + s[26] = byte(h[8] >> 4) + s[27] = byte(h[8] >> 12) + s[28] = byte((h[8] >> 20) | (h[9] << 6)) + s[29] = byte(h[9] >> 2) + s[30] = byte(h[9] >> 10) + s[31] = byte(h[9] >> 18) +} + +// feMul calculates h = f * g +// Can overlap h with f or g. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// |g| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +// +// Notes on implementation strategy: +// +// Using schoolbook multiplication. +// Karatsuba would save a little in some cost models. +// +// Most multiplications by 2 and 19 are 32-bit precomputations; +// cheaper than 64-bit postcomputations. +// +// There is one remaining multiplication by 19 in the carry chain; +// one *19 precomputation can be merged into this, +// but the resulting data flow is considerably less clean. +// +// There are 12 carries below. +// 10 of them are 2-way parallelizable and vectorizable. +// Can get away with 11 carries, but then data flow is much deeper. +// +// With tighter constraints on inputs can squeeze carries into int32. +func feMul(h, f, g *fieldElement) { + f0 := f[0] + f1 := f[1] + f2 := f[2] + f3 := f[3] + f4 := f[4] + f5 := f[5] + f6 := f[6] + f7 := f[7] + f8 := f[8] + f9 := f[9] + g0 := g[0] + g1 := g[1] + g2 := g[2] + g3 := g[3] + g4 := g[4] + g5 := g[5] + g6 := g[6] + g7 := g[7] + g8 := g[8] + g9 := g[9] + g1_19 := 19 * g1 // 1.4*2^29 + g2_19 := 19 * g2 // 1.4*2^30; still ok + g3_19 := 19 * g3 + g4_19 := 19 * g4 + g5_19 := 19 * g5 + g6_19 := 19 * g6 + g7_19 := 19 * g7 + g8_19 := 19 * g8 + g9_19 := 19 * g9 + f1_2 := 2 * f1 + f3_2 := 2 * f3 + f5_2 := 2 * f5 + f7_2 := 2 * f7 + f9_2 := 2 * f9 + f0g0 := int64(f0) * int64(g0) + f0g1 := int64(f0) * int64(g1) + f0g2 := int64(f0) * int64(g2) + f0g3 := int64(f0) * int64(g3) + f0g4 := int64(f0) * int64(g4) + f0g5 := int64(f0) * int64(g5) + f0g6 := int64(f0) * int64(g6) + f0g7 := int64(f0) * int64(g7) + f0g8 := int64(f0) * int64(g8) + f0g9 := int64(f0) * int64(g9) + f1g0 := int64(f1) * int64(g0) + f1g1_2 := int64(f1_2) * int64(g1) + f1g2 := int64(f1) * int64(g2) + f1g3_2 := int64(f1_2) * int64(g3) + f1g4 := int64(f1) * int64(g4) + f1g5_2 := int64(f1_2) * int64(g5) + f1g6 := int64(f1) * int64(g6) + f1g7_2 := int64(f1_2) * int64(g7) + f1g8 := int64(f1) * int64(g8) + f1g9_38 := int64(f1_2) * int64(g9_19) + f2g0 := int64(f2) * int64(g0) + f2g1 := int64(f2) * int64(g1) + f2g2 := int64(f2) * int64(g2) + f2g3 := int64(f2) * int64(g3) + f2g4 := int64(f2) * int64(g4) + f2g5 := int64(f2) * int64(g5) + f2g6 := int64(f2) * int64(g6) + f2g7 := int64(f2) * int64(g7) + f2g8_19 := int64(f2) * int64(g8_19) + f2g9_19 := int64(f2) * int64(g9_19) + f3g0 := int64(f3) * int64(g0) + f3g1_2 := int64(f3_2) * int64(g1) + f3g2 := int64(f3) * int64(g2) + f3g3_2 := int64(f3_2) * int64(g3) + f3g4 := int64(f3) * int64(g4) + f3g5_2 := int64(f3_2) * int64(g5) + f3g6 := int64(f3) * int64(g6) + f3g7_38 := int64(f3_2) * int64(g7_19) + f3g8_19 := int64(f3) * int64(g8_19) + f3g9_38 := int64(f3_2) * int64(g9_19) + f4g0 := int64(f4) * int64(g0) + f4g1 := int64(f4) * int64(g1) + f4g2 := int64(f4) * int64(g2) + f4g3 := int64(f4) * int64(g3) + f4g4 := int64(f4) * int64(g4) + f4g5 := int64(f4) * int64(g5) + f4g6_19 := int64(f4) * int64(g6_19) + f4g7_19 := int64(f4) * int64(g7_19) + f4g8_19 := int64(f4) * int64(g8_19) + f4g9_19 := int64(f4) * int64(g9_19) + f5g0 := int64(f5) * int64(g0) + f5g1_2 := int64(f5_2) * int64(g1) + f5g2 := int64(f5) * int64(g2) + f5g3_2 := int64(f5_2) * int64(g3) + f5g4 := int64(f5) * int64(g4) + f5g5_38 := int64(f5_2) * int64(g5_19) + f5g6_19 := int64(f5) * int64(g6_19) + f5g7_38 := int64(f5_2) * int64(g7_19) + f5g8_19 := int64(f5) * int64(g8_19) + f5g9_38 := int64(f5_2) * int64(g9_19) + f6g0 := int64(f6) * int64(g0) + f6g1 := int64(f6) * int64(g1) + f6g2 := int64(f6) * int64(g2) + f6g3 := int64(f6) * int64(g3) + f6g4_19 := int64(f6) * int64(g4_19) + f6g5_19 := int64(f6) * int64(g5_19) + f6g6_19 := int64(f6) * int64(g6_19) + f6g7_19 := int64(f6) * int64(g7_19) + f6g8_19 := int64(f6) * int64(g8_19) + f6g9_19 := int64(f6) * int64(g9_19) + f7g0 := int64(f7) * int64(g0) + f7g1_2 := int64(f7_2) * int64(g1) + f7g2 := int64(f7) * int64(g2) + f7g3_38 := int64(f7_2) * int64(g3_19) + f7g4_19 := int64(f7) * int64(g4_19) + f7g5_38 := int64(f7_2) * int64(g5_19) + f7g6_19 := int64(f7) * int64(g6_19) + f7g7_38 := int64(f7_2) * int64(g7_19) + f7g8_19 := int64(f7) * int64(g8_19) + f7g9_38 := int64(f7_2) * int64(g9_19) + f8g0 := int64(f8) * int64(g0) + f8g1 := int64(f8) * int64(g1) + f8g2_19 := int64(f8) * int64(g2_19) + f8g3_19 := int64(f8) * int64(g3_19) + f8g4_19 := int64(f8) * int64(g4_19) + f8g5_19 := int64(f8) * int64(g5_19) + f8g6_19 := int64(f8) * int64(g6_19) + f8g7_19 := int64(f8) * int64(g7_19) + f8g8_19 := int64(f8) * int64(g8_19) + f8g9_19 := int64(f8) * int64(g9_19) + f9g0 := int64(f9) * int64(g0) + f9g1_38 := int64(f9_2) * int64(g1_19) + f9g2_19 := int64(f9) * int64(g2_19) + f9g3_38 := int64(f9_2) * int64(g3_19) + f9g4_19 := int64(f9) * int64(g4_19) + f9g5_38 := int64(f9_2) * int64(g5_19) + f9g6_19 := int64(f9) * int64(g6_19) + f9g7_38 := int64(f9_2) * int64(g7_19) + f9g8_19 := int64(f9) * int64(g8_19) + f9g9_38 := int64(f9_2) * int64(g9_19) + h0 := f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38 + h1 := f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + f7g4_19 + f8g3_19 + f9g2_19 + h2 := f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + f7g5_38 + f8g4_19 + f9g3_38 + h3 := f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + f7g6_19 + f8g5_19 + f9g4_19 + h4 := f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + f7g7_38 + f8g6_19 + f9g5_38 + h5 := f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + f8g7_19 + f9g6_19 + h6 := f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + f7g9_38 + f8g8_19 + f9g7_38 + h7 := f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + f8g9_19 + f9g8_19 + h8 := f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + f8g0 + f9g9_38 + h9 := f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0 + var carry [10]int64 + + // |h0| <= (1.1*1.1*2^52*(1+19+19+19+19)+1.1*1.1*2^50*(38+38+38+38+38)) + // i.e. |h0| <= 1.2*2^59; narrower ranges for h2, h4, h6, h8 + // |h1| <= (1.1*1.1*2^51*(1+1+19+19+19+19+19+19+19+19)) + // i.e. |h1| <= 1.5*2^58; narrower ranges for h3, h5, h7, h9 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + // |h0| <= 2^25 + // |h4| <= 2^25 + // |h1| <= 1.51*2^58 + // |h5| <= 1.51*2^58 + + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + // |h1| <= 2^24; from now on fits into int32 + // |h5| <= 2^24; from now on fits into int32 + // |h2| <= 1.21*2^59 + // |h6| <= 1.21*2^59 + + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + // |h2| <= 2^25; from now on fits into int32 unchanged + // |h6| <= 2^25; from now on fits into int32 unchanged + // |h3| <= 1.51*2^58 + // |h7| <= 1.51*2^58 + + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + // |h3| <= 2^24; from now on fits into int32 unchanged + // |h7| <= 2^24; from now on fits into int32 unchanged + // |h4| <= 1.52*2^33 + // |h8| <= 1.52*2^33 + + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + // |h4| <= 2^25; from now on fits into int32 unchanged + // |h8| <= 2^25; from now on fits into int32 unchanged + // |h5| <= 1.01*2^24 + // |h9| <= 1.51*2^58 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + // |h9| <= 2^24; from now on fits into int32 unchanged + // |h0| <= 1.8*2^37 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + // |h0| <= 2^25; from now on fits into int32 unchanged + // |h1| <= 1.01*2^24 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feSquare calculates h = f*f. Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func feSquare(h, f *fieldElement) { + f0 := f[0] + f1 := f[1] + f2 := f[2] + f3 := f[3] + f4 := f[4] + f5 := f[5] + f6 := f[6] + f7 := f[7] + f8 := f[8] + f9 := f[9] + f0_2 := 2 * f0 + f1_2 := 2 * f1 + f2_2 := 2 * f2 + f3_2 := 2 * f3 + f4_2 := 2 * f4 + f5_2 := 2 * f5 + f6_2 := 2 * f6 + f7_2 := 2 * f7 + f5_38 := 38 * f5 // 1.31*2^30 + f6_19 := 19 * f6 // 1.31*2^30 + f7_38 := 38 * f7 // 1.31*2^30 + f8_19 := 19 * f8 // 1.31*2^30 + f9_38 := 38 * f9 // 1.31*2^30 + f0f0 := int64(f0) * int64(f0) + f0f1_2 := int64(f0_2) * int64(f1) + f0f2_2 := int64(f0_2) * int64(f2) + f0f3_2 := int64(f0_2) * int64(f3) + f0f4_2 := int64(f0_2) * int64(f4) + f0f5_2 := int64(f0_2) * int64(f5) + f0f6_2 := int64(f0_2) * int64(f6) + f0f7_2 := int64(f0_2) * int64(f7) + f0f8_2 := int64(f0_2) * int64(f8) + f0f9_2 := int64(f0_2) * int64(f9) + f1f1_2 := int64(f1_2) * int64(f1) + f1f2_2 := int64(f1_2) * int64(f2) + f1f3_4 := int64(f1_2) * int64(f3_2) + f1f4_2 := int64(f1_2) * int64(f4) + f1f5_4 := int64(f1_2) * int64(f5_2) + f1f6_2 := int64(f1_2) * int64(f6) + f1f7_4 := int64(f1_2) * int64(f7_2) + f1f8_2 := int64(f1_2) * int64(f8) + f1f9_76 := int64(f1_2) * int64(f9_38) + f2f2 := int64(f2) * int64(f2) + f2f3_2 := int64(f2_2) * int64(f3) + f2f4_2 := int64(f2_2) * int64(f4) + f2f5_2 := int64(f2_2) * int64(f5) + f2f6_2 := int64(f2_2) * int64(f6) + f2f7_2 := int64(f2_2) * int64(f7) + f2f8_38 := int64(f2_2) * int64(f8_19) + f2f9_38 := int64(f2) * int64(f9_38) + f3f3_2 := int64(f3_2) * int64(f3) + f3f4_2 := int64(f3_2) * int64(f4) + f3f5_4 := int64(f3_2) * int64(f5_2) + f3f6_2 := int64(f3_2) * int64(f6) + f3f7_76 := int64(f3_2) * int64(f7_38) + f3f8_38 := int64(f3_2) * int64(f8_19) + f3f9_76 := int64(f3_2) * int64(f9_38) + f4f4 := int64(f4) * int64(f4) + f4f5_2 := int64(f4_2) * int64(f5) + f4f6_38 := int64(f4_2) * int64(f6_19) + f4f7_38 := int64(f4) * int64(f7_38) + f4f8_38 := int64(f4_2) * int64(f8_19) + f4f9_38 := int64(f4) * int64(f9_38) + f5f5_38 := int64(f5) * int64(f5_38) + f5f6_38 := int64(f5_2) * int64(f6_19) + f5f7_76 := int64(f5_2) * int64(f7_38) + f5f8_38 := int64(f5_2) * int64(f8_19) + f5f9_76 := int64(f5_2) * int64(f9_38) + f6f6_19 := int64(f6) * int64(f6_19) + f6f7_38 := int64(f6) * int64(f7_38) + f6f8_38 := int64(f6_2) * int64(f8_19) + f6f9_38 := int64(f6) * int64(f9_38) + f7f7_38 := int64(f7) * int64(f7_38) + f7f8_38 := int64(f7_2) * int64(f8_19) + f7f9_76 := int64(f7_2) * int64(f9_38) + f8f8_19 := int64(f8) * int64(f8_19) + f8f9_38 := int64(f8) * int64(f9_38) + f9f9_38 := int64(f9) * int64(f9_38) + h0 := f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38 + h1 := f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38 + h2 := f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19 + h3 := f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38 + h4 := f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38 + h5 := f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38 + h6 := f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19 + h7 := f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38 + h8 := f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38 + h9 := f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2 + var carry [10]int64 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feMul121666 calculates h = f * 121666. Can overlap h with f. +// +// Preconditions: +// |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. +// +// Postconditions: +// |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. +func feMul121666(h, f *fieldElement) { + h0 := int64(f[0]) * 121666 + h1 := int64(f[1]) * 121666 + h2 := int64(f[2]) * 121666 + h3 := int64(f[3]) * 121666 + h4 := int64(f[4]) * 121666 + h5 := int64(f[5]) * 121666 + h6 := int64(f[6]) * 121666 + h7 := int64(f[7]) * 121666 + h8 := int64(f[8]) * 121666 + h9 := int64(f[9]) * 121666 + var carry [10]int64 + + carry[9] = (h9 + (1 << 24)) >> 25 + h0 += carry[9] * 19 + h9 -= carry[9] << 25 + carry[1] = (h1 + (1 << 24)) >> 25 + h2 += carry[1] + h1 -= carry[1] << 25 + carry[3] = (h3 + (1 << 24)) >> 25 + h4 += carry[3] + h3 -= carry[3] << 25 + carry[5] = (h5 + (1 << 24)) >> 25 + h6 += carry[5] + h5 -= carry[5] << 25 + carry[7] = (h7 + (1 << 24)) >> 25 + h8 += carry[7] + h7 -= carry[7] << 25 + + carry[0] = (h0 + (1 << 25)) >> 26 + h1 += carry[0] + h0 -= carry[0] << 26 + carry[2] = (h2 + (1 << 25)) >> 26 + h3 += carry[2] + h2 -= carry[2] << 26 + carry[4] = (h4 + (1 << 25)) >> 26 + h5 += carry[4] + h4 -= carry[4] << 26 + carry[6] = (h6 + (1 << 25)) >> 26 + h7 += carry[6] + h6 -= carry[6] << 26 + carry[8] = (h8 + (1 << 25)) >> 26 + h9 += carry[8] + h8 -= carry[8] << 26 + + h[0] = int32(h0) + h[1] = int32(h1) + h[2] = int32(h2) + h[3] = int32(h3) + h[4] = int32(h4) + h[5] = int32(h5) + h[6] = int32(h6) + h[7] = int32(h7) + h[8] = int32(h8) + h[9] = int32(h9) +} + +// feInvert sets out = z^-1. +func feInvert(out, z *fieldElement) { + var t0, t1, t2, t3 fieldElement + var i int + + feSquare(&t0, z) + for i = 1; i < 1; i++ { + feSquare(&t0, &t0) + } + feSquare(&t1, &t0) + for i = 1; i < 2; i++ { + feSquare(&t1, &t1) + } + feMul(&t1, z, &t1) + feMul(&t0, &t0, &t1) + feSquare(&t2, &t0) + for i = 1; i < 1; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t1, &t2) + feSquare(&t2, &t1) + for i = 1; i < 5; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t2, &t1) + for i = 1; i < 10; i++ { + feSquare(&t2, &t2) + } + feMul(&t2, &t2, &t1) + feSquare(&t3, &t2) + for i = 1; i < 20; i++ { + feSquare(&t3, &t3) + } + feMul(&t2, &t3, &t2) + feSquare(&t2, &t2) + for i = 1; i < 10; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t2, &t1) + for i = 1; i < 50; i++ { + feSquare(&t2, &t2) + } + feMul(&t2, &t2, &t1) + feSquare(&t3, &t2) + for i = 1; i < 100; i++ { + feSquare(&t3, &t3) + } + feMul(&t2, &t3, &t2) + feSquare(&t2, &t2) + for i = 1; i < 50; i++ { + feSquare(&t2, &t2) + } + feMul(&t1, &t2, &t1) + feSquare(&t1, &t1) + for i = 1; i < 5; i++ { + feSquare(&t1, &t1) + } + feMul(out, &t1, &t0) +} + +func scalarMult(out, in, base *[32]byte) { + var e [32]byte + + copy(e[:], in[:]) + e[0] &= 248 + e[31] &= 127 + e[31] |= 64 + + var x1, x2, z2, x3, z3, tmp0, tmp1 fieldElement + feFromBytes(&x1, base) + feOne(&x2) + feCopy(&x3, &x1) + feOne(&z3) + + swap := int32(0) + for pos := 254; pos >= 0; pos-- { + b := e[pos/8] >> uint(pos&7) + b &= 1 + swap ^= int32(b) + feCSwap(&x2, &x3, swap) + feCSwap(&z2, &z3, swap) + swap = int32(b) + + feSub(&tmp0, &x3, &z3) + feSub(&tmp1, &x2, &z2) + feAdd(&x2, &x2, &z2) + feAdd(&z2, &x3, &z3) + feMul(&z3, &tmp0, &x2) + feMul(&z2, &z2, &tmp1) + feSquare(&tmp0, &tmp1) + feSquare(&tmp1, &x2) + feAdd(&x3, &z3, &z2) + feSub(&z2, &z3, &z2) + feMul(&x2, &tmp1, &tmp0) + feSub(&tmp1, &tmp1, &tmp0) + feSquare(&z2, &z2) + feMul121666(&z3, &tmp1) + feSquare(&x3, &x3) + feAdd(&tmp0, &tmp0, &z3) + feMul(&z3, &x1, &z2) + feMul(&z2, &tmp1, &tmp0) + } + + feCSwap(&x2, &x3, swap) + feCSwap(&z2, &z3, swap) + + feInvert(&z2, &z2) + feMul(&x2, &x2, &z2) + feToBytes(out, &x2) +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/curve25519_test.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/curve25519_test.go new file mode 100644 index 000000000..14b0ee87c --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/curve25519_test.go @@ -0,0 +1,29 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package curve25519 + +import ( + "fmt" + "testing" +) + +const expectedHex = "89161fde887b2b53de549af483940106ecc114d6982daa98256de23bdf77661a" + +func TestBaseScalarMult(t *testing.T) { + var a, b [32]byte + in := &a + out := &b + a[0] = 1 + + for i := 0; i < 200; i++ { + ScalarBaseMult(out, in) + in, out = out, in + } + + result := fmt.Sprintf("%x", in[:]) + if result != expectedHex { + t.Errorf("incorrect result: got %s, want %s", result, expectedHex) + } +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/doc.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/doc.go new file mode 100644 index 000000000..ebeea3c2d --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/doc.go @@ -0,0 +1,23 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package curve25519 provides an implementation of scalar multiplication on +// the elliptic curve known as curve25519. See http://cr.yp.to/ecdh.html +package curve25519 // import "golang.org/x/crypto/curve25519" + +// basePoint is the x coordinate of the generator of the curve. +var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +// ScalarMult sets dst to the product in*base where dst and base are the x +// coordinates of group points and all values are in little-endian form. +func ScalarMult(dst, in, base *[32]byte) { + scalarMult(dst, in, base) +} + +// ScalarBaseMult sets dst to the product in*base where dst and base are the x +// coordinates of group points, base is the standard generator and all values +// are in little-endian form. +func ScalarBaseMult(dst, in *[32]byte) { + ScalarMult(dst, in, &basePoint) +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/mont25519_amd64.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/mont25519_amd64.go new file mode 100644 index 000000000..5822bd533 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/curve25519/mont25519_amd64.go @@ -0,0 +1,240 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine + +package curve25519 + +// These functions are implemented in the .s files. The names of the functions +// in the rest of the file are also taken from the SUPERCOP sources to help +// people following along. + +//go:noescape + +func cswap(inout *[5]uint64, v uint64) + +//go:noescape + +func ladderstep(inout *[5][5]uint64) + +//go:noescape + +func freeze(inout *[5]uint64) + +//go:noescape + +func mul(dest, a, b *[5]uint64) + +//go:noescape + +func square(out, in *[5]uint64) + +// mladder uses a Montgomery ladder to calculate (xr/zr) *= s. +func mladder(xr, zr *[5]uint64, s *[32]byte) { + var work [5][5]uint64 + + work[0] = *xr + setint(&work[1], 1) + setint(&work[2], 0) + work[3] = *xr + setint(&work[4], 1) + + j := uint(6) + var prevbit byte + + for i := 31; i >= 0; i-- { + for j < 8 { + bit := ((*s)[i] >> j) & 1 + swap := bit ^ prevbit + prevbit = bit + cswap(&work[1], uint64(swap)) + ladderstep(&work) + j-- + } + j = 7 + } + + *xr = work[1] + *zr = work[2] +} + +func scalarMult(out, in, base *[32]byte) { + var e [32]byte + copy(e[:], (*in)[:]) + e[0] &= 248 + e[31] &= 127 + e[31] |= 64 + + var t, z [5]uint64 + unpack(&t, base) + mladder(&t, &z, &e) + invert(&z, &z) + mul(&t, &t, &z) + pack(out, &t) +} + +func setint(r *[5]uint64, v uint64) { + r[0] = v + r[1] = 0 + r[2] = 0 + r[3] = 0 + r[4] = 0 +} + +// unpack sets r = x where r consists of 5, 51-bit limbs in little-endian +// order. +func unpack(r *[5]uint64, x *[32]byte) { + r[0] = uint64(x[0]) | + uint64(x[1])<<8 | + uint64(x[2])<<16 | + uint64(x[3])<<24 | + uint64(x[4])<<32 | + uint64(x[5])<<40 | + uint64(x[6]&7)<<48 + + r[1] = uint64(x[6])>>3 | + uint64(x[7])<<5 | + uint64(x[8])<<13 | + uint64(x[9])<<21 | + uint64(x[10])<<29 | + uint64(x[11])<<37 | + uint64(x[12]&63)<<45 + + r[2] = uint64(x[12])>>6 | + uint64(x[13])<<2 | + uint64(x[14])<<10 | + uint64(x[15])<<18 | + uint64(x[16])<<26 | + uint64(x[17])<<34 | + uint64(x[18])<<42 | + uint64(x[19]&1)<<50 + + r[3] = uint64(x[19])>>1 | + uint64(x[20])<<7 | + uint64(x[21])<<15 | + uint64(x[22])<<23 | + uint64(x[23])<<31 | + uint64(x[24])<<39 | + uint64(x[25]&15)<<47 + + r[4] = uint64(x[25])>>4 | + uint64(x[26])<<4 | + uint64(x[27])<<12 | + uint64(x[28])<<20 | + uint64(x[29])<<28 | + uint64(x[30])<<36 | + uint64(x[31]&127)<<44 +} + +// pack sets out = x where out is the usual, little-endian form of the 5, +// 51-bit limbs in x. +func pack(out *[32]byte, x *[5]uint64) { + t := *x + freeze(&t) + + out[0] = byte(t[0]) + out[1] = byte(t[0] >> 8) + out[2] = byte(t[0] >> 16) + out[3] = byte(t[0] >> 24) + out[4] = byte(t[0] >> 32) + out[5] = byte(t[0] >> 40) + out[6] = byte(t[0] >> 48) + + out[6] ^= byte(t[1]<<3) & 0xf8 + out[7] = byte(t[1] >> 5) + out[8] = byte(t[1] >> 13) + out[9] = byte(t[1] >> 21) + out[10] = byte(t[1] >> 29) + out[11] = byte(t[1] >> 37) + out[12] = byte(t[1] >> 45) + + out[12] ^= byte(t[2]<<6) & 0xc0 + out[13] = byte(t[2] >> 2) + out[14] = byte(t[2] >> 10) + out[15] = byte(t[2] >> 18) + out[16] = byte(t[2] >> 26) + out[17] = byte(t[2] >> 34) + out[18] = byte(t[2] >> 42) + out[19] = byte(t[2] >> 50) + + out[19] ^= byte(t[3]<<1) & 0xfe + out[20] = byte(t[3] >> 7) + out[21] = byte(t[3] >> 15) + out[22] = byte(t[3] >> 23) + out[23] = byte(t[3] >> 31) + out[24] = byte(t[3] >> 39) + out[25] = byte(t[3] >> 47) + + out[25] ^= byte(t[4]<<4) & 0xf0 + out[26] = byte(t[4] >> 4) + out[27] = byte(t[4] >> 12) + out[28] = byte(t[4] >> 20) + out[29] = byte(t[4] >> 28) + out[30] = byte(t[4] >> 36) + out[31] = byte(t[4] >> 44) +} + +// invert calculates r = x^-1 mod p using Fermat's little theorem. +func invert(r *[5]uint64, x *[5]uint64) { + var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t [5]uint64 + + square(&z2, x) /* 2 */ + square(&t, &z2) /* 4 */ + square(&t, &t) /* 8 */ + mul(&z9, &t, x) /* 9 */ + mul(&z11, &z9, &z2) /* 11 */ + square(&t, &z11) /* 22 */ + mul(&z2_5_0, &t, &z9) /* 2^5 - 2^0 = 31 */ + + square(&t, &z2_5_0) /* 2^6 - 2^1 */ + for i := 1; i < 5; i++ { /* 2^20 - 2^10 */ + square(&t, &t) + } + mul(&z2_10_0, &t, &z2_5_0) /* 2^10 - 2^0 */ + + square(&t, &z2_10_0) /* 2^11 - 2^1 */ + for i := 1; i < 10; i++ { /* 2^20 - 2^10 */ + square(&t, &t) + } + mul(&z2_20_0, &t, &z2_10_0) /* 2^20 - 2^0 */ + + square(&t, &z2_20_0) /* 2^21 - 2^1 */ + for i := 1; i < 20; i++ { /* 2^40 - 2^20 */ + square(&t, &t) + } + mul(&t, &t, &z2_20_0) /* 2^40 - 2^0 */ + + square(&t, &t) /* 2^41 - 2^1 */ + for i := 1; i < 10; i++ { /* 2^50 - 2^10 */ + square(&t, &t) + } + mul(&z2_50_0, &t, &z2_10_0) /* 2^50 - 2^0 */ + + square(&t, &z2_50_0) /* 2^51 - 2^1 */ + for i := 1; i < 50; i++ { /* 2^100 - 2^50 */ + square(&t, &t) + } + mul(&z2_100_0, &t, &z2_50_0) /* 2^100 - 2^0 */ + + square(&t, &z2_100_0) /* 2^101 - 2^1 */ + for i := 1; i < 100; i++ { /* 2^200 - 2^100 */ + square(&t, &t) + } + mul(&t, &t, &z2_100_0) /* 2^200 - 2^0 */ + + square(&t, &t) /* 2^201 - 2^1 */ + for i := 1; i < 50; i++ { /* 2^250 - 2^50 */ + square(&t, &t) + } + mul(&t, &t, &z2_50_0) /* 2^250 - 2^0 */ + + square(&t, &t) /* 2^251 - 2^1 */ + square(&t, &t) /* 2^252 - 2^2 */ + square(&t, &t) /* 2^253 - 2^3 */ + + square(&t, &t) /* 2^254 - 2^4 */ + + square(&t, &t) /* 2^255 - 2^5 */ + mul(r, &t, &z11) /* 2^255 - 21 */ +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/deterministic.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/deterministic.go new file mode 100644 index 000000000..2b71076b1 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/deterministic.go @@ -0,0 +1,37 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "encoding/binary" +) + +// Use a different key from crypto/rand/deterministic.c. +var deterministicRandKey = []byte("runner deterministic key 0123456") + +type deterministicRand struct { + numCalls uint64 +} + +func (d *deterministicRand) Read(buf []byte) (int, error) { + for i := range buf { + buf[i] = 0 + } + var nonce [12]byte + binary.LittleEndian.PutUint64(nonce[:8], d.numCalls) + chaCha20(buf, buf, deterministicRandKey, nonce[:], 0) + d.numCalls++ + return len(buf), nil +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/dtls.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/dtls.go new file mode 100644 index 000000000..e273bc796 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/dtls.go @@ -0,0 +1,499 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// DTLS implementation. +// +// NOTE: This is a not even a remotely production-quality DTLS +// implementation. It is the bare minimum necessary to be able to +// achieve coverage on BoringSSL's implementation. Of note is that +// this implementation assumes the underlying net.PacketConn is not +// only reliable but also ordered. BoringSSL will be expected to deal +// with simulated loss, but there is no point in forcing the test +// driver to. + +package runner + +import ( + "bytes" + "errors" + "fmt" + "io" + "math/rand" + "net" +) + +func versionToWire(vers uint16, isDTLS bool) uint16 { + if isDTLS { + switch vers { + case VersionTLS12: + return 0xfefd + case VersionTLS10: + return 0xfeff + } + } else { + switch vers { + case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: + return vers + case VersionTLS13: + return tls13DraftVersion + } + } + + panic("unknown version") +} + +func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) { + if isDTLS { + switch vers { + case 0xfefd: + return VersionTLS12, true + case 0xfeff: + return VersionTLS10, true + } + } else { + switch vers { + case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12: + return vers, true + case tls13DraftVersion: + return VersionTLS13, true + } + } + + return 0, false +} + +func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) { + recordHeaderLen := dtlsRecordHeaderLen + + if c.rawInput == nil { + c.rawInput = c.in.newBlock() + } + b := c.rawInput + + // Read a new packet only if the current one is empty. + var newPacket bool + if len(b.data) == 0 { + // Pick some absurdly large buffer size. + b.resize(maxCiphertext + recordHeaderLen) + n, err := c.conn.Read(c.rawInput.data) + if err != nil { + return 0, nil, err + } + if c.config.Bugs.MaxPacketLength != 0 && n > c.config.Bugs.MaxPacketLength { + return 0, nil, fmt.Errorf("dtls: exceeded maximum packet length") + } + c.rawInput.resize(n) + newPacket = true + } + + // Read out one record. + // + // A real DTLS implementation should be tolerant of errors, + // but this is test code. We should not be tolerant of our + // peer sending garbage. + if len(b.data) < recordHeaderLen { + return 0, nil, errors.New("dtls: failed to read record header") + } + typ := recordType(b.data[0]) + vers := uint16(b.data[1])<<8 | uint16(b.data[2]) + // Alerts sent near version negotiation do not have a well-defined + // record-layer version prior to TLS 1.3. (In TLS 1.3, the record-layer + // version is irrelevant.) + if typ != recordTypeAlert { + if c.haveVers { + if wireVers := versionToWire(c.vers, c.isDTLS); vers != wireVers { + c.sendAlert(alertProtocolVersion) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, wireVers)) + } + } else { + // Pre-version-negotiation alerts may be sent with any version. + if expect := c.config.Bugs.ExpectInitialRecordVersion; expect != 0 && vers != expect { + c.sendAlert(alertProtocolVersion) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, expect)) + } + } + } + epoch := b.data[3:5] + seq := b.data[5:11] + // For test purposes, require the sequence number be monotonically + // increasing, so c.in includes the minimum next sequence number. Gaps + // may occur if packets failed to be sent out. A real implementation + // would maintain a replay window and such. + if !bytes.Equal(epoch, c.in.seq[:2]) { + c.sendAlert(alertIllegalParameter) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad epoch")) + } + if bytes.Compare(seq, c.in.seq[2:]) < 0 { + c.sendAlert(alertIllegalParameter) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad sequence number")) + } + copy(c.in.seq[2:], seq) + n := int(b.data[11])<<8 | int(b.data[12]) + if n > maxCiphertext || len(b.data) < recordHeaderLen+n { + c.sendAlert(alertRecordOverflow) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: oversized record received with length %d", n)) + } + + // Process message. + b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) + ok, off, _, alertValue := c.in.decrypt(b) + if !ok { + // A real DTLS implementation would silently ignore bad records, + // but we want to notice errors from the implementation under + // test. + return 0, nil, c.in.setErrorLocked(c.sendAlert(alertValue)) + } + b.off = off + + // TODO(nharper): Once DTLS 1.3 is defined, handle the extra + // parameter from decrypt. + + // Require that ChangeCipherSpec always share a packet with either the + // previous or next handshake message. + if newPacket && typ == recordTypeChangeCipherSpec && c.rawInput == nil { + return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: ChangeCipherSpec not packed together with Finished")) + } + + return typ, b, nil +} + +func (c *Conn) makeFragment(header, data []byte, fragOffset, fragLen int) []byte { + fragment := make([]byte, 0, 12+fragLen) + fragment = append(fragment, header...) + fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq)) + fragment = append(fragment, byte(fragOffset>>16), byte(fragOffset>>8), byte(fragOffset)) + fragment = append(fragment, byte(fragLen>>16), byte(fragLen>>8), byte(fragLen)) + fragment = append(fragment, data[fragOffset:fragOffset+fragLen]...) + return fragment +} + +func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) { + if typ != recordTypeHandshake { + // Only handshake messages are fragmented. + n, err = c.dtlsWriteRawRecord(typ, data) + if err != nil { + return + } + + if typ == recordTypeChangeCipherSpec { + err = c.out.changeCipherSpec(c.config) + if err != nil { + // Cannot call sendAlert directly, + // because we already hold c.out.Mutex. + c.tmp[0] = alertLevelError + c.tmp[1] = byte(err.(alert)) + c.writeRecord(recordTypeAlert, c.tmp[0:2]) + return n, c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err}) + } + } + return + } + + if c.out.cipher == nil && c.config.Bugs.StrayChangeCipherSpec { + _, err = c.dtlsWriteRawRecord(recordTypeChangeCipherSpec, []byte{1}) + if err != nil { + return + } + } + + maxLen := c.config.Bugs.MaxHandshakeRecordLength + if maxLen <= 0 { + maxLen = 1024 + } + + // Handshake messages have to be modified to include fragment + // offset and length and with the header replicated. Save the + // TLS header here. + // + // TODO(davidben): This assumes that data contains exactly one + // handshake message. This is incompatible with + // FragmentAcrossChangeCipherSpec. (Which is unfortunate + // because OpenSSL's DTLS implementation will probably accept + // such fragmentation and could do with a fix + tests.) + header := data[:4] + data = data[4:] + + isFinished := header[0] == typeFinished + + if c.config.Bugs.SendEmptyFragments { + fragment := c.makeFragment(header, data, 0, 0) + c.pendingFragments = append(c.pendingFragments, fragment) + } + + firstRun := true + fragOffset := 0 + for firstRun || fragOffset < len(data) { + firstRun = false + fragLen := len(data) - fragOffset + if fragLen > maxLen { + fragLen = maxLen + } + + fragment := c.makeFragment(header, data, fragOffset, fragLen) + if c.config.Bugs.FragmentMessageTypeMismatch && fragOffset > 0 { + fragment[0]++ + } + if c.config.Bugs.FragmentMessageLengthMismatch && fragOffset > 0 { + fragment[3]++ + } + + // Buffer the fragment for later. They will be sent (and + // reordered) on flush. + c.pendingFragments = append(c.pendingFragments, fragment) + if c.config.Bugs.ReorderHandshakeFragments { + // Don't duplicate Finished to avoid the peer + // interpreting it as a retransmit request. + if !isFinished { + c.pendingFragments = append(c.pendingFragments, fragment) + } + + if fragLen > (maxLen+1)/2 { + // Overlap each fragment by half. + fragLen = (maxLen + 1) / 2 + } + } + fragOffset += fragLen + n += fragLen + } + if !isFinished && c.config.Bugs.MixCompleteMessageWithFragments { + fragment := c.makeFragment(header, data, 0, len(data)) + c.pendingFragments = append(c.pendingFragments, fragment) + } + + // Increment the handshake sequence number for the next + // handshake message. + c.sendHandshakeSeq++ + return +} + +func (c *Conn) dtlsFlushHandshake() error { + // This is a test-only DTLS implementation, so there is no need to + // retain |c.pendingFragments| for a future retransmit. + var fragments [][]byte + fragments, c.pendingFragments = c.pendingFragments, fragments + + if c.config.Bugs.ReorderHandshakeFragments { + perm := rand.New(rand.NewSource(0)).Perm(len(fragments)) + tmp := make([][]byte, len(fragments)) + for i := range tmp { + tmp[i] = fragments[perm[i]] + } + fragments = tmp + } else if c.config.Bugs.ReverseHandshakeFragments { + tmp := make([][]byte, len(fragments)) + for i := range tmp { + tmp[i] = fragments[len(fragments)-i-1] + } + fragments = tmp + } + + maxRecordLen := c.config.Bugs.PackHandshakeFragments + maxPacketLen := c.config.Bugs.PackHandshakeRecords + + // Pack handshake fragments into records. + var records [][]byte + for _, fragment := range fragments { + if n := c.config.Bugs.SplitFragments; n > 0 { + if len(fragment) > n { + records = append(records, fragment[:n]) + records = append(records, fragment[n:]) + } else { + records = append(records, fragment) + } + } else if i := len(records) - 1; len(records) > 0 && len(records[i])+len(fragment) <= maxRecordLen { + records[i] = append(records[i], fragment...) + } else { + // The fragment will be appended to, so copy it. + records = append(records, append([]byte{}, fragment...)) + } + } + + // Format them into packets. + var packets [][]byte + for _, record := range records { + b, err := c.dtlsSealRecord(recordTypeHandshake, record) + if err != nil { + return err + } + + if i := len(packets) - 1; len(packets) > 0 && len(packets[i])+len(b.data) <= maxPacketLen { + packets[i] = append(packets[i], b.data...) + } else { + // The sealed record will be appended to and reused by + // |c.out|, so copy it. + packets = append(packets, append([]byte{}, b.data...)) + } + c.out.freeBlock(b) + } + + // Send all the packets. + for _, packet := range packets { + if _, err := c.conn.Write(packet); err != nil { + return err + } + } + return nil +} + +// dtlsSealRecord seals a record into a block from |c.out|'s pool. +func (c *Conn) dtlsSealRecord(typ recordType, data []byte) (b *block, err error) { + recordHeaderLen := dtlsRecordHeaderLen + maxLen := c.config.Bugs.MaxHandshakeRecordLength + if maxLen <= 0 { + maxLen = 1024 + } + + b = c.out.newBlock() + + explicitIVLen := 0 + explicitIVIsSeq := false + + if cbc, ok := c.out.cipher.(cbcMode); ok { + // Block cipher modes have an explicit IV. + explicitIVLen = cbc.BlockSize() + } else if aead, ok := c.out.cipher.(*tlsAead); ok { + if aead.explicitNonce { + explicitIVLen = 8 + // The AES-GCM construction in TLS has an explicit nonce so that + // the nonce can be random. However, the nonce is only 8 bytes + // which is too small for a secure, random nonce. Therefore we + // use the sequence number as the nonce. + explicitIVIsSeq = true + } + } else if _, ok := c.out.cipher.(nullCipher); !ok && c.out.cipher != nil { + panic("Unknown cipher") + } + b.resize(recordHeaderLen + explicitIVLen + len(data)) + // TODO(nharper): DTLS 1.3 will likely need to set this to + // recordTypeApplicationData if c.out.cipher != nil. + b.data[0] = byte(typ) + vers := c.vers + if vers == 0 { + // Some TLS servers fail if the record version is greater than + // TLS 1.0 for the initial ClientHello. + vers = VersionTLS10 + } + vers = versionToWire(vers, c.isDTLS) + b.data[1] = byte(vers >> 8) + b.data[2] = byte(vers) + // DTLS records include an explicit sequence number. + copy(b.data[3:11], c.out.outSeq[0:]) + b.data[11] = byte(len(data) >> 8) + b.data[12] = byte(len(data)) + if explicitIVLen > 0 { + explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + if explicitIVIsSeq { + copy(explicitIV, c.out.outSeq[:]) + } else { + if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { + return + } + } + } + copy(b.data[recordHeaderLen+explicitIVLen:], data) + c.out.encrypt(b, explicitIVLen, typ) + return +} + +func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) { + b, err := c.dtlsSealRecord(typ, data) + if err != nil { + return + } + + _, err = c.conn.Write(b.data) + if err != nil { + return + } + n = len(data) + + c.out.freeBlock(b) + return +} + +func (c *Conn) dtlsDoReadHandshake() ([]byte, error) { + // Assemble a full handshake message. For test purposes, this + // implementation assumes fragments arrive in order. It may + // need to be cleverer if we ever test BoringSSL's retransmit + // behavior. + for len(c.handMsg) < 4+c.handMsgLen { + // Get a new handshake record if the previous has been + // exhausted. + if c.hand.Len() == 0 { + if err := c.in.err; err != nil { + return nil, err + } + if err := c.readRecord(recordTypeHandshake); err != nil { + return nil, err + } + } + + // Read the next fragment. It must fit entirely within + // the record. + if c.hand.Len() < 12 { + return nil, errors.New("dtls: bad handshake record") + } + header := c.hand.Next(12) + fragN := int(header[1])<<16 | int(header[2])<<8 | int(header[3]) + fragSeq := uint16(header[4])<<8 | uint16(header[5]) + fragOff := int(header[6])<<16 | int(header[7])<<8 | int(header[8]) + fragLen := int(header[9])<<16 | int(header[10])<<8 | int(header[11]) + + if c.hand.Len() < fragLen { + return nil, errors.New("dtls: fragment length too long") + } + fragment := c.hand.Next(fragLen) + + // Check it's a fragment for the right message. + if fragSeq != c.recvHandshakeSeq { + return nil, errors.New("dtls: bad handshake sequence number") + } + + // Check that the length is consistent. + if c.handMsg == nil { + c.handMsgLen = fragN + if c.handMsgLen > maxHandshake { + return nil, c.in.setErrorLocked(c.sendAlert(alertInternalError)) + } + // Start with the TLS handshake header, + // without the DTLS bits. + c.handMsg = append([]byte{}, header[:4]...) + } else if fragN != c.handMsgLen { + return nil, errors.New("dtls: bad handshake length") + } + + // Add the fragment to the pending message. + if 4+fragOff != len(c.handMsg) { + return nil, errors.New("dtls: bad fragment offset") + } + if fragOff+fragLen > c.handMsgLen { + return nil, errors.New("dtls: bad fragment length") + } + c.handMsg = append(c.handMsg, fragment...) + } + c.recvHandshakeSeq++ + ret := c.handMsg + c.handMsg, c.handMsgLen = nil, 0 + return ret, nil +} + +// DTLSServer returns a new DTLS server side connection +// using conn as the underlying transport. +// The configuration config must be non-nil and must have +// at least one certificate. +func DTLSServer(conn net.Conn, config *Config) *Conn { + c := &Conn{config: config, isDTLS: true, conn: conn} + c.init() + return c +} + +// DTLSClient returns a new DTLS client side connection +// using conn as the underlying transport. +// The config cannot be nil: users must set either ServerHostname or +// InsecureSkipVerify in the config. +func DTLSClient(conn net.Conn, config *Config) *Conn { + c := &Conn{config: config, isClient: true, isDTLS: true, conn: conn} + c.init() + return c +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p224_cert.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p224_cert.pem new file mode 100644 index 000000000..29246a2ea --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p224_cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBvjCCAWygAwIBAgIJAPlkrPTq4HgnMAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT +AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn +aXRzIFB0eSBMdGQwHhcNMTcwMjI3MjAxOTIzWhcNMTkwMjI3MjAxOTIzWjBFMQsw +CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu +ZXQgV2lkZ2l0cyBQdHkgTHRkME4wEAYHKoZIzj0CAQYFK4EEACEDOgAE6dul6dL0 ++CyooFiKK4V+EYNYPbMZoLTxRcjRgrw3db6QzBAviDSxKADTVuyRmaVC74Mf6boB +HDmjUDBOMB0GA1UdDgQWBBSMtlkUJ7SCZ4zRqkjXMWvOebSgpTAfBgNVHSMEGDAW +gBSMtlkUJ7SCZ4zRqkjXMWvOebSgpTAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMC +A0AAMD0CHHolWPktSLbVMy9ukQUb2E7+Jb3hcNFqAXh47pYCHQC+jv2EE6oOEZ9F +tLkFLtap71+83P0NUEJX4Etu +-----END CERTIFICATE----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p224_key.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p224_key.pem new file mode 100644 index 000000000..cfe411b45 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p224_key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MGgCAQEEHGi+rNLi+gHJqmRRtdlLBOw1WYv7H/VnlYGAZ0+gBwYFK4EEACGhPAM6 +AATp26Xp0vT4LKigWIorhX4Rg1g9sxmgtPFFyNGCvDd1vpDMEC+INLEoANNW7JGZ +pULvgx/pugEcOQ== +-----END EC PRIVATE KEY----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p256_cert.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p256_cert.pem new file mode 100644 index 000000000..50bcbf5bf --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p256_cert.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBzzCCAXagAwIBAgIJANlMBNpJfb/rMAkGByqGSM49BAEwRTELMAkGA1UEBhMC +QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp +dHMgUHR5IEx0ZDAeFw0xNDA0MjMyMzIxNTdaFw0xNDA1MjMyMzIxNTdaMEUxCzAJ +BgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATmK2ni +v2Wfl74vHg2UikzVl2u3qR4NRvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYa +HPUdfvGULUvPciLBo1AwTjAdBgNVHQ4EFgQUq4TSrKuV8IJOFngHVVdf5CaNgtEw +HwYDVR0jBBgwFoAUq4TSrKuV8IJOFngHVVdf5CaNgtEwDAYDVR0TBAUwAwEB/zAJ +BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E +BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ= +-----END CERTIFICATE----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p256_key.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p256_key.pem new file mode 100644 index 000000000..bce6a1e15 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p256_key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBw8IcnrUoEqc3VnJ +TYlodwi1b8ldMHcO6NHJzgqLtGqhRANCAATmK2niv2Wfl74vHg2UikzVl2u3qR4N +Rvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLB +-----END PRIVATE KEY----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p384_cert.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p384_cert.pem new file mode 100644 index 000000000..1fd3fec63 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p384_cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICZTCCAeugAwIBAgIJAN+/LubpDwxNMAkGByqGSM49BAEwRTELMAkGA1UEBhMC +QVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp +dHMgUHR5IEx0ZDAeFw0xNjA3MDkwMDAxMzJaFw0xNjA4MDgwMDAxMzJaMEUxCzAJ +BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQOdTJNqxiZ ++B68tCZV4GEJwDJ18jK9gFzvefcEAQluBijjrMjflL+RZAT64ExWzedRMp9PD9CW +Tz9hG/Kz4q/l952YsIhy7LTGXzwy7549WUOi+N3aW8psDjtwzWNZXqWjgacwgaQw +HQYDVR0OBBYEFKmYPjADcOlogOMU6D9wlftIWMj6MHUGA1UdIwRuMGyAFKmYPjAD +cOlogOMU6D9wlftIWMj6oUmkRzBFMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29t +ZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkggkA378u +5ukPDE0wDAYDVR0TBAUwAwEB/zAJBgcqhkjOPQQBA2kAMGYCMQDTfL0OkRGnS5Ze +tsxagAuZqM2Zyv5a2g7u6eFLCx2rpTuQndWOtEnmVo3wjTDtkDcCMQCg+05XSqEF +cqxdXMZJMhqj2jS+tWucdgDstp/1KzJkbsupSjBzIycjVBKLdRwtNg8= +-----END CERTIFICATE----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p384_key.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p384_key.pem new file mode 100644 index 000000000..d72e10451 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p384_key.pem @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCVFIFNZ6y5EijVMUuD +W+WJsQRAVxsZ+6ge1Nez2IoOr3+APs6+fj130OJinFATXa2hZANiAAQOdTJNqxiZ ++B68tCZV4GEJwDJ18jK9gFzvefcEAQluBijjrMjflL+RZAT64ExWzedRMp9PD9CW +Tz9hG/Kz4q/l952YsIhy7LTGXzwy7549WUOi+N3aW8psDjtwzWNZXqU= +-----END PRIVATE KEY----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p521_cert.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p521_cert.pem new file mode 100644 index 000000000..8b9a1e838 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p521_cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICrzCCAhGgAwIBAgIJAMZT7oSqTg/lMAkGByqGSM49BAEwRTELMAkGA1UEBhMC +QVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdp +dHMgUHR5IEx0ZDAeFw0xNjA3MDkwMDAwNTBaFw0xNjA4MDgwMDAwNTBaMEUxCzAJ +BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABAFD0TiZ +InCoCYDI66FvoaZ0tniUhaNk2YnjEPikfmYHDwstbqkTEqnGAq3pKF2y/MHTBXFd +E0KcFOPs5Ju8EgIcqwGvCkwHPmZtvvOrf+nJNHdm1gZfBUy09f3fVe/mHwfM2++H +dz54nHKxtQqvdafhmMV77R/rBuI24MLKbMihxRgQKaOBpzCBpDAdBgNVHQ4EFgQU +mLWm6ZtGfvih6iFx0+duYfHjGsYwdQYDVR0jBG4wbIAUmLWm6ZtGfvih6iFx0+du +YfHjGsahSaRHMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEw +HwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGSCCQDGU+6Eqk4P5TAMBgNV +HRMEBTADAQH/MAkGByqGSM49BAEDgYwAMIGIAkIBL/oEXMMQH9fefVd8onvgQbWI +/CJXYE+kLO15gO8satasGutVpXtYGeP8gZeuHrq+jWxzj7dGM7YzkW0pAu+wOAkC +QgFJSvKFcmbreLIjuwYik5aXeaUnTCvTQumG07cF0hZRgCf/kTxxmu2ffRoGCDTz +XoPqlxwaO7K+gaAS//CvR0E3lw== +-----END CERTIFICATE----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p521_key.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p521_key.pem new file mode 100644 index 000000000..a8b1006c1 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ecdsa_p521_key.pem @@ -0,0 +1,7 @@ +-----BEGIN PRIVATE KEY----- +MIHtAgEAMBAGByqGSM49AgEGBSuBBAAjBIHVMIHSAgEBBEFqpd7S4UQqO2MJSylA +8Ufq/qbUuebEJY6t1Oz76JZ0u0k+NdmmujWjULcP4thAhX+xitUap+r86ud8FQCv +dawUnKGBiQOBhgAEAUPROJkicKgJgMjroW+hpnS2eJSFo2TZieMQ+KR+ZgcPCy1u +qRMSqcYCrekoXbL8wdMFcV0TQpwU4+zkm7wSAhyrAa8KTAc+Zm2+86t/6ck0d2bW +Bl8FTLT1/d9V7+YfB8zb74d3PniccrG1Cq91p+GYxXvtH+sG4jbgwspsyKHFGBAp +-----END PRIVATE KEY----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/fuzzer_mode.json b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/fuzzer_mode.json new file mode 100644 index 000000000..80a9bf1de --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/fuzzer_mode.json @@ -0,0 +1,39 @@ +{ + "DisabledTests": { + "BadCBCPadding*": "Fuzzer mode has no CBC padding.", + + "BadFinished-*": "Fuzzer mode ignores Finished checks.", + "FalseStart-BadFinished": "Fuzzer mode ignores Finished checks.", + "TrailingMessageData-*Finished*": "Fuzzer mode ignores Finished checks.", + + "DTLSIgnoreBadPackets*": "Fuzzer mode has no bad packets.", + "TLSFatalBadPackets": "Fuzzer mode has no bad packets.", + "*-BadRecord": "Fuzzer mode has no bad packets.", + + "BadRSAClientKeyExchange*": "Fuzzer mode does not notice a bad premaster secret.", + + "TrailingMessageData-TLS13-ServerHello": "Fuzzer mode will not read the peer's alert as a MAC error", + "UnexpectedUnencryptedExtension-Client-TLS13": "Fuzzer mode will not read the peer's alert as a MAC error", + "UnknownUnencryptedExtension-Client-TLS13": "Fuzzer mode will not read the peer's alert as a MAC error", + "WrongMessageType-TLS13-ServerHello": "Fuzzer mode will not read the peer's alert as a MAC error", + + "BadECDSA-*": "Fuzzer mode always accepts a signature.", + "*-InvalidSignature-*": "Fuzzer mode always accepts a signature.", + "*Auth-Verify-RSA-PKCS1-*-TLS13": "Fuzzer mode always accepts a signature.", + "*Auth-Verify-ECDSA-SHA1-TLS13": "Fuzzer mode always accepts a signature.", + "Verify-*Auth-SignatureType*": "Fuzzer mode always accepts a signature.", + "ECDSACurveMismatch-Verify-TLS13": "Fuzzer mode always accepts a signature.", + "InvalidChannelIDSignature-*": "Fuzzer mode always accepts a signature.", + + "Resume-Server-CipherNotPreferred*": "Fuzzer mode does not encrypt tickets.", + "Resume-Server-DeclineBadCipher*": "Fuzzer mode does not encrypt tickets.", + "Resume-Server-DeclineCrossVersion*": "Fuzzer mode does not encrypt tickets.", + "TicketCallback-SingleCall-*": "Fuzzer mode does not encrypt tickets.", + "CorruptTicket-*": "Fuzzer mode does not encrypt tickets.", + "ShimTicketRewritable": "Fuzzer mode does not encrypt tickets.", + + "Resume-Server-*Binder*": "Fuzzer mode does not check binders.", + + "SkipEarlyData*": "Trial decryption does not work with the NULL cipher." + } +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_client.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_client.go new file mode 100644 index 000000000..bf38c1a78 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_client.go @@ -0,0 +1,1758 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/subtle" + "crypto/x509" + "errors" + "fmt" + "io" + "math/big" + "net" + "strconv" + "time" +) + +type clientHandshakeState struct { + c *Conn + serverHello *serverHelloMsg + hello *clientHelloMsg + suite *cipherSuite + finishedHash finishedHash + keyShares map[CurveID]ecdhCurve + masterSecret []byte + session *ClientSessionState + finishedBytes []byte +} + +func (c *Conn) clientHandshake() error { + if c.config == nil { + c.config = defaultConfig() + } + + if len(c.config.ServerName) == 0 && !c.config.InsecureSkipVerify { + return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") + } + + c.sendHandshakeSeq = 0 + c.recvHandshakeSeq = 0 + + nextProtosLength := 0 + for _, proto := range c.config.NextProtos { + if l := len(proto); l > 255 { + return errors.New("tls: invalid NextProtos value") + } else { + nextProtosLength += 1 + l + } + } + if nextProtosLength > 0xffff { + return errors.New("tls: NextProtos values too large") + } + + minVersion := c.config.minVersion(c.isDTLS) + maxVersion := c.config.maxVersion(c.isDTLS) + hello := &clientHelloMsg{ + isDTLS: c.isDTLS, + vers: versionToWire(maxVersion, c.isDTLS), + compressionMethods: []uint8{compressionNone}, + random: make([]byte, 32), + ocspStapling: !c.config.Bugs.NoOCSPStapling, + sctListSupported: !c.config.Bugs.NoSignedCertificateTimestamps, + serverName: c.config.ServerName, + supportedCurves: c.config.curvePreferences(), + pskKEModes: []byte{pskDHEKEMode}, + supportedPoints: []uint8{pointFormatUncompressed}, + nextProtoNeg: len(c.config.NextProtos) > 0, + secureRenegotiation: []byte{}, + alpnProtocols: c.config.NextProtos, + duplicateExtension: c.config.Bugs.DuplicateExtension, + channelIDSupported: c.config.ChannelID != nil, + npnAfterAlpn: c.config.Bugs.SwapNPNAndALPN, + extendedMasterSecret: maxVersion >= VersionTLS10, + srtpProtectionProfiles: c.config.SRTPProtectionProfiles, + srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer, + customExtension: c.config.Bugs.CustomExtension, + pskBinderFirst: c.config.Bugs.PSKBinderFirst, + } + + disableEMS := c.config.Bugs.NoExtendedMasterSecret + if c.cipherSuite != nil { + disableEMS = c.config.Bugs.NoExtendedMasterSecretOnRenegotiation + } + + if disableEMS { + hello.extendedMasterSecret = false + } + + if c.config.Bugs.NoSupportedCurves { + hello.supportedCurves = nil + } + + if len(c.config.Bugs.SendPSKKeyExchangeModes) != 0 { + hello.pskKEModes = c.config.Bugs.SendPSKKeyExchangeModes + } + + if c.config.Bugs.SendCompressionMethods != nil { + hello.compressionMethods = c.config.Bugs.SendCompressionMethods + } + + if c.config.Bugs.SendSupportedPointFormats != nil { + hello.supportedPoints = c.config.Bugs.SendSupportedPointFormats + } + + if len(c.clientVerify) > 0 && !c.config.Bugs.EmptyRenegotiationInfo { + if c.config.Bugs.BadRenegotiationInfo { + hello.secureRenegotiation = append(hello.secureRenegotiation, c.clientVerify...) + hello.secureRenegotiation[0] ^= 0x80 + } else { + hello.secureRenegotiation = c.clientVerify + } + } + + if c.noRenegotiationInfo() { + hello.secureRenegotiation = nil + } + + var keyShares map[CurveID]ecdhCurve + if maxVersion >= VersionTLS13 { + keyShares = make(map[CurveID]ecdhCurve) + hello.hasKeyShares = true + hello.trailingKeyShareData = c.config.Bugs.TrailingKeyShareData + curvesToSend := c.config.defaultCurves() + for _, curveID := range hello.supportedCurves { + if !curvesToSend[curveID] { + continue + } + curve, ok := curveForCurveID(curveID) + if !ok { + continue + } + publicKey, err := curve.offer(c.config.rand()) + if err != nil { + return err + } + + if c.config.Bugs.SendCurve != 0 { + curveID = c.config.Bugs.SendCurve + } + if c.config.Bugs.InvalidECDHPoint { + publicKey[0] ^= 0xff + } + + hello.keyShares = append(hello.keyShares, keyShareEntry{ + group: curveID, + keyExchange: publicKey, + }) + keyShares[curveID] = curve + + if c.config.Bugs.DuplicateKeyShares { + hello.keyShares = append(hello.keyShares, hello.keyShares[len(hello.keyShares)-1]) + } + } + + if c.config.Bugs.MissingKeyShare { + hello.hasKeyShares = false + } + } + + possibleCipherSuites := c.config.cipherSuites() + hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites)) + +NextCipherSuite: + for _, suiteId := range possibleCipherSuites { + for _, suite := range cipherSuites { + if suite.id != suiteId { + continue + } + // Don't advertise TLS 1.2-only cipher suites unless + // we're attempting TLS 1.2. + if maxVersion < VersionTLS12 && suite.flags&suiteTLS12 != 0 { + continue + } + // Don't advertise non-DTLS cipher suites in DTLS. + if c.isDTLS && suite.flags&suiteNoDTLS != 0 { + continue + } + hello.cipherSuites = append(hello.cipherSuites, suiteId) + continue NextCipherSuite + } + } + + if c.config.Bugs.AdvertiseAllConfiguredCiphers { + hello.cipherSuites = possibleCipherSuites + } + + if c.config.Bugs.SendRenegotiationSCSV { + hello.cipherSuites = append(hello.cipherSuites, renegotiationSCSV) + } + + if c.config.Bugs.SendFallbackSCSV { + hello.cipherSuites = append(hello.cipherSuites, fallbackSCSV) + } + + _, err := io.ReadFull(c.config.rand(), hello.random) + if err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: short read from Rand: " + err.Error()) + } + + if maxVersion >= VersionTLS12 && !c.config.Bugs.NoSignatureAlgorithms { + hello.signatureAlgorithms = c.config.verifySignatureAlgorithms() + } + + var session *ClientSessionState + var cacheKey string + sessionCache := c.config.ClientSessionCache + + if sessionCache != nil { + hello.ticketSupported = !c.config.SessionTicketsDisabled + + // Try to resume a previously negotiated TLS session, if + // available. + cacheKey = clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + // TODO(nharper): Support storing more than one session + // ticket for TLS 1.3. + candidateSession, ok := sessionCache.Get(cacheKey) + if ok { + ticketOk := !c.config.SessionTicketsDisabled || candidateSession.sessionTicket == nil + + // Check that the ciphersuite/version used for the + // previous session are still valid. + cipherSuiteOk := false + if candidateSession.vers <= VersionTLS12 { + for _, id := range hello.cipherSuites { + if id == candidateSession.cipherSuite { + cipherSuiteOk = true + break + } + } + } else { + // TLS 1.3 allows the cipher to change on + // resumption. + cipherSuiteOk = true + } + + versOk := candidateSession.vers >= minVersion && + candidateSession.vers <= maxVersion + if ticketOk && versOk && cipherSuiteOk { + session = candidateSession + } + } + } + + var pskCipherSuite *cipherSuite + if session != nil && c.config.time().Before(session.ticketExpiration) { + ticket := session.sessionTicket + if c.config.Bugs.FilterTicket != nil && len(ticket) > 0 { + // Copy the ticket so FilterTicket may act in-place. + ticket = make([]byte, len(session.sessionTicket)) + copy(ticket, session.sessionTicket) + + ticket, err = c.config.Bugs.FilterTicket(ticket) + if err != nil { + return err + } + } + + if session.vers >= VersionTLS13 || c.config.Bugs.SendBothTickets { + pskCipherSuite = cipherSuiteFromID(session.cipherSuite) + if pskCipherSuite == nil { + return errors.New("tls: client session cache has invalid cipher suite") + } + // TODO(nharper): Support sending more + // than one PSK identity. + ticketAge := uint32(c.config.time().Sub(session.ticketCreationTime) / time.Millisecond) + if c.config.Bugs.SendTicketAge != 0 { + ticketAge = uint32(c.config.Bugs.SendTicketAge / time.Millisecond) + } + psk := pskIdentity{ + ticket: ticket, + obfuscatedTicketAge: session.ticketAgeAdd + ticketAge, + } + hello.pskIdentities = []pskIdentity{psk} + + if c.config.Bugs.ExtraPSKIdentity { + hello.pskIdentities = append(hello.pskIdentities, psk) + } + } + + if session.vers < VersionTLS13 || c.config.Bugs.SendBothTickets { + if ticket != nil { + hello.sessionTicket = ticket + // A random session ID is used to detect when the + // server accepted the ticket and is resuming a session + // (see RFC 5077). + sessionIdLen := 16 + if c.config.Bugs.TicketSessionIDLength != 0 { + sessionIdLen = c.config.Bugs.TicketSessionIDLength + } + if c.config.Bugs.EmptyTicketSessionID { + sessionIdLen = 0 + } + hello.sessionId = make([]byte, sessionIdLen) + if _, err := io.ReadFull(c.config.rand(), hello.sessionId); err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: short read from Rand: " + err.Error()) + } + } else { + hello.sessionId = session.sessionId + } + } + } + + if maxVersion == VersionTLS13 && !c.config.Bugs.OmitSupportedVersions { + if hello.vers >= VersionTLS13 { + hello.vers = VersionTLS12 + } + for version := maxVersion; version >= minVersion; version-- { + hello.supportedVersions = append(hello.supportedVersions, versionToWire(version, c.isDTLS)) + } + } + + if len(c.config.Bugs.SendSupportedVersions) > 0 { + hello.supportedVersions = c.config.Bugs.SendSupportedVersions + } + + if c.config.Bugs.SendClientVersion != 0 { + hello.vers = c.config.Bugs.SendClientVersion + } + + if c.config.Bugs.SendCipherSuites != nil { + hello.cipherSuites = c.config.Bugs.SendCipherSuites + } + + var sendEarlyData bool + if len(hello.pskIdentities) > 0 && session.maxEarlyDataSize > 0 && c.config.Bugs.SendEarlyData != nil { + hello.hasEarlyData = true + sendEarlyData = true + } + if c.config.Bugs.SendFakeEarlyDataLength > 0 { + hello.hasEarlyData = true + } + if c.config.Bugs.OmitEarlyDataExtension { + hello.hasEarlyData = false + } + + var helloBytes []byte + if c.config.Bugs.SendV2ClientHello { + // Test that the peer left-pads random. + hello.random[0] = 0 + v2Hello := &v2ClientHelloMsg{ + vers: hello.vers, + cipherSuites: hello.cipherSuites, + // No session resumption for V2ClientHello. + sessionId: nil, + challenge: hello.random[1:], + } + helloBytes = v2Hello.marshal() + c.writeV2Record(helloBytes) + } else { + if len(hello.pskIdentities) > 0 { + generatePSKBinders(hello, pskCipherSuite, session.masterSecret, []byte{}, c.config) + } + helloBytes = hello.marshal() + + if c.config.Bugs.PartialClientFinishedWithClientHello { + // Include one byte of Finished. We can compute it + // without completing the handshake. This assumes we + // negotiate TLS 1.3 with no HelloRetryRequest or + // CertificateRequest. + toWrite := make([]byte, 0, len(helloBytes)+1) + toWrite = append(toWrite, helloBytes...) + toWrite = append(toWrite, typeFinished) + c.writeRecord(recordTypeHandshake, toWrite) + } else { + c.writeRecord(recordTypeHandshake, helloBytes) + } + } + c.flushHandshake() + + if err := c.simulatePacketLoss(nil); err != nil { + return err + } + if c.config.Bugs.SendEarlyAlert { + c.sendAlert(alertHandshakeFailure) + } + if c.config.Bugs.SendFakeEarlyDataLength > 0 { + c.sendFakeEarlyData(c.config.Bugs.SendFakeEarlyDataLength) + } + + // Derive early write keys and set Conn state to allow early writes. + if sendEarlyData { + finishedHash := newFinishedHash(session.vers, pskCipherSuite) + finishedHash.addEntropy(session.masterSecret) + finishedHash.Write(helloBytes) + earlyTrafficSecret := finishedHash.deriveSecret(earlyTrafficLabel) + c.out.useTrafficSecret(session.vers, pskCipherSuite, earlyTrafficSecret, clientWrite) + + for _, earlyData := range c.config.Bugs.SendEarlyData { + if _, err := c.writeRecord(recordTypeApplicationData, earlyData); err != nil { + return err + } + } + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + if c.isDTLS { + helloVerifyRequest, ok := msg.(*helloVerifyRequestMsg) + if ok { + if helloVerifyRequest.vers != versionToWire(VersionTLS10, c.isDTLS) { + // Per RFC 6347, the version field in + // HelloVerifyRequest SHOULD be always DTLS + // 1.0. Enforce this for testing purposes. + return errors.New("dtls: bad HelloVerifyRequest version") + } + + hello.raw = nil + hello.cookie = helloVerifyRequest.cookie + helloBytes = hello.marshal() + c.writeRecord(recordTypeHandshake, helloBytes) + c.flushHandshake() + + if err := c.simulatePacketLoss(nil); err != nil { + return err + } + msg, err = c.readHandshake() + if err != nil { + return err + } + } + } + + var serverWireVersion uint16 + switch m := msg.(type) { + case *helloRetryRequestMsg: + serverWireVersion = m.vers + case *serverHelloMsg: + serverWireVersion = m.vers + default: + c.sendAlert(alertUnexpectedMessage) + return fmt.Errorf("tls: received unexpected message of type %T when waiting for HelloRetryRequest or ServerHello", msg) + } + + serverVersion, ok := wireToVersion(serverWireVersion, c.isDTLS) + if ok { + ok = c.config.isSupportedVersion(serverVersion, c.isDTLS) + } + if !ok { + c.sendAlert(alertProtocolVersion) + return fmt.Errorf("tls: server selected unsupported protocol version %x", c.vers) + } + c.vers = serverVersion + c.haveVers = true + + helloRetryRequest, haveHelloRetryRequest := msg.(*helloRetryRequestMsg) + var secondHelloBytes []byte + if haveHelloRetryRequest { + c.out.resetCipher() + if len(helloRetryRequest.cookie) > 0 { + hello.tls13Cookie = helloRetryRequest.cookie + } + + if c.config.Bugs.MisinterpretHelloRetryRequestCurve != 0 { + helloRetryRequest.hasSelectedGroup = true + helloRetryRequest.selectedGroup = c.config.Bugs.MisinterpretHelloRetryRequestCurve + } + if helloRetryRequest.hasSelectedGroup { + var hrrCurveFound bool + group := helloRetryRequest.selectedGroup + for _, curveID := range hello.supportedCurves { + if group == curveID { + hrrCurveFound = true + break + } + } + if !hrrCurveFound || keyShares[group] != nil { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: received invalid HelloRetryRequest") + } + curve, ok := curveForCurveID(group) + if !ok { + return errors.New("tls: Unable to get curve requested in HelloRetryRequest") + } + publicKey, err := curve.offer(c.config.rand()) + if err != nil { + return err + } + keyShares[group] = curve + hello.keyShares = []keyShareEntry{{ + group: group, + keyExchange: publicKey, + }} + } + + if c.config.Bugs.SecondClientHelloMissingKeyShare { + hello.hasKeyShares = false + } + + hello.hasEarlyData = c.config.Bugs.SendEarlyDataOnSecondClientHello + hello.raw = nil + + if len(hello.pskIdentities) > 0 { + generatePSKBinders(hello, pskCipherSuite, session.masterSecret, append(helloBytes, helloRetryRequest.marshal()...), c.config) + } + secondHelloBytes = hello.marshal() + + if c.config.Bugs.InterleaveEarlyData { + c.sendFakeEarlyData(4) + c.writeRecord(recordTypeHandshake, secondHelloBytes[:16]) + c.sendFakeEarlyData(4) + c.writeRecord(recordTypeHandshake, secondHelloBytes[16:]) + } else { + c.writeRecord(recordTypeHandshake, secondHelloBytes) + } + c.flushHandshake() + + if c.config.Bugs.SendEarlyDataOnSecondClientHello { + c.sendFakeEarlyData(4) + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + serverHello, ok := msg.(*serverHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverHello, msg) + } + + if serverWireVersion != serverHello.vers { + c.sendAlert(alertProtocolVersion) + return fmt.Errorf("tls: server sent non-matching version %x vs %x", serverWireVersion, serverHello.vers) + } + + // Check for downgrade signals in the server random, per + // draft-ietf-tls-tls13-16, section 4.1.3. + if c.vers <= VersionTLS12 && c.config.maxVersion(c.isDTLS) >= VersionTLS13 { + if bytes.Equal(serverHello.random[len(serverHello.random)-8:], downgradeTLS13) { + c.sendAlert(alertProtocolVersion) + return errors.New("tls: downgrade from TLS 1.3 detected") + } + } + if c.vers <= VersionTLS11 && c.config.maxVersion(c.isDTLS) >= VersionTLS12 { + if bytes.Equal(serverHello.random[len(serverHello.random)-8:], downgradeTLS12) { + c.sendAlert(alertProtocolVersion) + return errors.New("tls: downgrade from TLS 1.2 detected") + } + } + + suite := mutualCipherSuite(hello.cipherSuites, serverHello.cipherSuite) + if suite == nil { + c.sendAlert(alertHandshakeFailure) + return fmt.Errorf("tls: server selected an unsupported cipher suite") + } + + if haveHelloRetryRequest && helloRetryRequest.hasSelectedGroup && helloRetryRequest.selectedGroup != serverHello.keyShare.group { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: ServerHello parameters did not match HelloRetryRequest") + } + + hs := &clientHandshakeState{ + c: c, + serverHello: serverHello, + hello: hello, + suite: suite, + finishedHash: newFinishedHash(c.vers, suite), + keyShares: keyShares, + session: session, + } + + hs.writeHash(helloBytes, hs.c.sendHandshakeSeq-1) + if haveHelloRetryRequest { + hs.writeServerHash(helloRetryRequest.marshal()) + hs.writeClientHash(secondHelloBytes) + } + hs.writeServerHash(hs.serverHello.marshal()) + + if c.vers >= VersionTLS13 { + if err := hs.doTLS13Handshake(); err != nil { + return err + } + } else { + if c.config.Bugs.EarlyChangeCipherSpec > 0 { + hs.establishKeys() + c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + } + + if hs.serverHello.compressionMethod != compressionNone { + c.sendAlert(alertUnexpectedMessage) + return errors.New("tls: server selected unsupported compression format") + } + + err = hs.processServerExtensions(&serverHello.extensions) + if err != nil { + return err + } + + isResume, err := hs.processServerHello() + if err != nil { + return err + } + + if isResume { + if c.config.Bugs.EarlyChangeCipherSpec == 0 { + if err := hs.establishKeys(); err != nil { + return err + } + } + if err := hs.readSessionTicket(); err != nil { + return err + } + if err := hs.readFinished(c.firstFinished[:]); err != nil { + return err + } + if err := hs.sendFinished(nil, isResume); err != nil { + return err + } + } else { + if err := hs.doFullHandshake(); err != nil { + return err + } + if err := hs.establishKeys(); err != nil { + return err + } + if err := hs.sendFinished(c.firstFinished[:], isResume); err != nil { + return err + } + // Most retransmits are triggered by a timeout, but the final + // leg of the handshake is retransmited upon re-receiving a + // Finished. + if err := c.simulatePacketLoss(func() { + c.sendHandshakeSeq-- + c.writeRecord(recordTypeHandshake, hs.finishedBytes) + c.flushHandshake() + }); err != nil { + return err + } + if err := hs.readSessionTicket(); err != nil { + return err + } + if err := hs.readFinished(nil); err != nil { + return err + } + } + + if sessionCache != nil && hs.session != nil && session != hs.session { + if c.config.Bugs.RequireSessionTickets && len(hs.session.sessionTicket) == 0 { + return errors.New("tls: new session used session IDs instead of tickets") + } + sessionCache.Put(cacheKey, hs.session) + } + + c.didResume = isResume + c.exporterSecret = hs.masterSecret + } + + c.handshakeComplete = true + c.cipherSuite = suite + copy(c.clientRandom[:], hs.hello.random) + copy(c.serverRandom[:], hs.serverHello.random) + + return nil +} + +func (hs *clientHandshakeState) doTLS13Handshake() error { + c := hs.c + + // Once the PRF hash is known, TLS 1.3 does not require a handshake + // buffer. + hs.finishedHash.discardHandshakeBuffer() + + zeroSecret := hs.finishedHash.zeroSecret() + + // Resolve PSK and compute the early secret. + // + // TODO(davidben): This will need to be handled slightly earlier once + // 0-RTT is implemented. + if hs.serverHello.hasPSKIdentity { + // We send at most one PSK identity. + if hs.session == nil || hs.serverHello.pskIdentity != 0 { + c.sendAlert(alertUnknownPSKIdentity) + return errors.New("tls: server sent unknown PSK identity") + } + sessionCipher := cipherSuiteFromID(hs.session.cipherSuite) + if sessionCipher == nil || sessionCipher.hash() != hs.suite.hash() { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: server resumed an invalid session for the cipher suite") + } + hs.finishedHash.addEntropy(hs.session.masterSecret) + c.didResume = true + } else { + hs.finishedHash.addEntropy(zeroSecret) + } + + if !hs.serverHello.hasKeyShare { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: server omitted KeyShare on resumption.") + } + + // Resolve ECDHE and compute the handshake secret. + if !c.config.Bugs.MissingKeyShare && !c.config.Bugs.SecondClientHelloMissingKeyShare { + curve, ok := hs.keyShares[hs.serverHello.keyShare.group] + if !ok { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: server selected an unsupported group") + } + c.curveID = hs.serverHello.keyShare.group + + ecdheSecret, err := curve.finish(hs.serverHello.keyShare.keyExchange) + if err != nil { + return err + } + hs.finishedHash.addEntropy(ecdheSecret) + } else { + hs.finishedHash.addEntropy(zeroSecret) + } + + // Derive handshake traffic keys and switch read key to handshake + // traffic key. + clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel) + serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel) + c.in.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite) + + msg, err := c.readHandshake() + if err != nil { + return err + } + + encryptedExtensions, ok := msg.(*encryptedExtensionsMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(encryptedExtensions, msg) + } + hs.writeServerHash(encryptedExtensions.marshal()) + + err = hs.processServerExtensions(&encryptedExtensions.extensions) + if err != nil { + return err + } + + var chainToSend *Certificate + var certReq *certificateRequestMsg + if c.didResume { + // Copy over authentication from the session. + c.peerCertificates = hs.session.serverCertificates + c.sctList = hs.session.sctList + c.ocspResponse = hs.session.ocspResponse + } else { + msg, err := c.readHandshake() + if err != nil { + return err + } + + var ok bool + certReq, ok = msg.(*certificateRequestMsg) + if ok { + if len(certReq.requestContext) != 0 { + return errors.New("tls: non-empty certificate request context sent in handshake") + } + + if c.config.Bugs.IgnorePeerSignatureAlgorithmPreferences { + certReq.signatureAlgorithms = c.config.signSignatureAlgorithms() + } + + hs.writeServerHash(certReq.marshal()) + + chainToSend, err = selectClientCertificate(c, certReq) + if err != nil { + return err + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + certMsg, ok := msg.(*certificateMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + hs.writeServerHash(certMsg.marshal()) + + // Check for unsolicited extensions. + for i, cert := range certMsg.certificates { + if c.config.Bugs.NoOCSPStapling && cert.ocspResponse != nil { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: unexpected OCSP response in the server certificate") + } + if c.config.Bugs.NoSignedCertificateTimestamps && cert.sctList != nil { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: unexpected SCT list in the server certificate") + } + if i > 0 && c.config.Bugs.ExpectNoExtensionsOnIntermediate && (cert.ocspResponse != nil || cert.sctList != nil) { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: unexpected extensions in the server certificate") + } + } + + if err := hs.verifyCertificates(certMsg); err != nil { + return err + } + leaf := c.peerCertificates[0] + c.ocspResponse = certMsg.certificates[0].ocspResponse + c.sctList = certMsg.certificates[0].sctList + + msg, err = c.readHandshake() + if err != nil { + return err + } + certVerifyMsg, ok := msg.(*certificateVerifyMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certVerifyMsg, msg) + } + + c.peerSignatureAlgorithm = certVerifyMsg.signatureAlgorithm + input := hs.finishedHash.certificateVerifyInput(serverCertificateVerifyContextTLS13) + err = verifyMessage(c.vers, leaf.PublicKey, c.config, certVerifyMsg.signatureAlgorithm, input, certVerifyMsg.signature) + if err != nil { + return err + } + + hs.writeServerHash(certVerifyMsg.marshal()) + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + serverFinished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverFinished, msg) + } + + verify := hs.finishedHash.serverSum(serverHandshakeTrafficSecret) + if len(verify) != len(serverFinished.verifyData) || + subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: server's Finished message was incorrect") + } + + hs.writeServerHash(serverFinished.marshal()) + + // The various secrets do not incorporate the client's final leg, so + // derive them now before updating the handshake context. + hs.finishedHash.addEntropy(zeroSecret) + clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel) + serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel) + c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel) + + // Switch to application data keys on read. In particular, any alerts + // from the client certificate are read over these keys. + c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite) + + // If we're expecting 0.5-RTT messages from the server, read them + // now. + for _, expectedMsg := range c.config.Bugs.ExpectHalfRTTData { + if err := c.readRecord(recordTypeApplicationData); err != nil { + return err + } + if !bytes.Equal(c.input.data[c.input.off:], expectedMsg) { + return errors.New("ExpectHalfRTTData: did not get expected message") + } + c.in.freeBlock(c.input) + c.input = nil + } + + // Send EndOfEarlyData and then switch write key to handshake + // traffic key. + if c.out.cipher != nil { + c.sendAlert(alertEndOfEarlyData) + } + c.out.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite) + + if certReq != nil && !c.config.Bugs.SkipClientCertificate { + certMsg := &certificateMsg{ + hasRequestContext: true, + requestContext: certReq.requestContext, + } + if chainToSend != nil { + for _, certData := range chainToSend.Certificate { + certMsg.certificates = append(certMsg.certificates, certificateEntry{ + data: certData, + extraExtension: c.config.Bugs.SendExtensionOnCertificate, + }) + } + } + hs.writeClientHash(certMsg.marshal()) + c.writeRecord(recordTypeHandshake, certMsg.marshal()) + + if chainToSend != nil { + certVerify := &certificateVerifyMsg{ + hasSignatureAlgorithm: true, + } + + // Determine the hash to sign. + privKey := chainToSend.PrivateKey + + var err error + certVerify.signatureAlgorithm, err = selectSignatureAlgorithm(c.vers, privKey, c.config, certReq.signatureAlgorithms) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + input := hs.finishedHash.certificateVerifyInput(clientCertificateVerifyContextTLS13) + certVerify.signature, err = signMessage(c.vers, privKey, c.config, certVerify.signatureAlgorithm, input) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + if c.config.Bugs.SendSignatureAlgorithm != 0 { + certVerify.signatureAlgorithm = c.config.Bugs.SendSignatureAlgorithm + } + + hs.writeClientHash(certVerify.marshal()) + c.writeRecord(recordTypeHandshake, certVerify.marshal()) + } + } + + if encryptedExtensions.extensions.channelIDRequested { + channelIDHash := crypto.SHA256.New() + channelIDHash.Write(hs.finishedHash.certificateVerifyInput(channelIDContextTLS13)) + channelIDMsgBytes, err := hs.writeChannelIDMessage(channelIDHash.Sum(nil)) + if err != nil { + return err + } + hs.writeClientHash(channelIDMsgBytes) + c.writeRecord(recordTypeHandshake, channelIDMsgBytes) + } + + // Send a client Finished message. + finished := new(finishedMsg) + finished.verifyData = hs.finishedHash.clientSum(clientHandshakeTrafficSecret) + if c.config.Bugs.BadFinished { + finished.verifyData[0]++ + } + hs.writeClientHash(finished.marshal()) + if c.config.Bugs.PartialClientFinishedWithClientHello { + // The first byte has already been sent. + c.writeRecord(recordTypeHandshake, finished.marshal()[1:]) + } else if c.config.Bugs.InterleaveEarlyData { + finishedBytes := finished.marshal() + c.sendFakeEarlyData(4) + c.writeRecord(recordTypeHandshake, finishedBytes[:1]) + c.sendFakeEarlyData(4) + c.writeRecord(recordTypeHandshake, finishedBytes[1:]) + } else { + c.writeRecord(recordTypeHandshake, finished.marshal()) + } + if c.config.Bugs.SendExtraFinished { + c.writeRecord(recordTypeHandshake, finished.marshal()) + } + c.flushHandshake() + + // Switch to application data keys. + c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite) + + c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel) + return nil +} + +func (hs *clientHandshakeState) doFullHandshake() error { + c := hs.c + + var leaf *x509.Certificate + if hs.suite.flags&suitePSK == 0 { + msg, err := c.readHandshake() + if err != nil { + return err + } + + certMsg, ok := msg.(*certificateMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + hs.writeServerHash(certMsg.marshal()) + + if err := hs.verifyCertificates(certMsg); err != nil { + return err + } + leaf = c.peerCertificates[0] + } + + if hs.serverHello.extensions.ocspStapling { + msg, err := c.readHandshake() + if err != nil { + return err + } + cs, ok := msg.(*certificateStatusMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(cs, msg) + } + hs.writeServerHash(cs.marshal()) + + if cs.statusType == statusTypeOCSP { + c.ocspResponse = cs.response + } + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + keyAgreement := hs.suite.ka(c.vers) + + skx, ok := msg.(*serverKeyExchangeMsg) + if ok { + hs.writeServerHash(skx.marshal()) + err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, leaf, skx) + if err != nil { + c.sendAlert(alertUnexpectedMessage) + return err + } + if ecdhe, ok := keyAgreement.(*ecdheKeyAgreement); ok { + c.curveID = ecdhe.curveID + } + + c.peerSignatureAlgorithm = keyAgreement.peerSignatureAlgorithm() + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + var chainToSend *Certificate + var certRequested bool + certReq, ok := msg.(*certificateRequestMsg) + if ok { + certRequested = true + if c.config.Bugs.IgnorePeerSignatureAlgorithmPreferences { + certReq.signatureAlgorithms = c.config.signSignatureAlgorithms() + } + + hs.writeServerHash(certReq.marshal()) + + chainToSend, err = selectClientCertificate(c, certReq) + if err != nil { + return err + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + shd, ok := msg.(*serverHelloDoneMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(shd, msg) + } + hs.writeServerHash(shd.marshal()) + + // If the server requested a certificate then we have to send a + // Certificate message in TLS, even if it's empty because we don't have + // a certificate to send. In SSL 3.0, skip the message and send a + // no_certificate warning alert. + if certRequested { + if c.vers == VersionSSL30 && chainToSend == nil { + c.sendAlert(alertNoCertificate) + } else if !c.config.Bugs.SkipClientCertificate { + certMsg := new(certificateMsg) + if chainToSend != nil { + for _, certData := range chainToSend.Certificate { + certMsg.certificates = append(certMsg.certificates, certificateEntry{ + data: certData, + }) + } + } + hs.writeClientHash(certMsg.marshal()) + c.writeRecord(recordTypeHandshake, certMsg.marshal()) + } + } + + preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, leaf) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + if ckx != nil { + if c.config.Bugs.EarlyChangeCipherSpec < 2 { + hs.writeClientHash(ckx.marshal()) + } + c.writeRecord(recordTypeHandshake, ckx.marshal()) + } + + if hs.serverHello.extensions.extendedMasterSecret && c.vers >= VersionTLS10 { + hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash) + c.extendedMasterSecret = true + } else { + if c.config.Bugs.RequireExtendedMasterSecret { + return errors.New("tls: extended master secret required but not supported by peer") + } + hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random) + } + + if chainToSend != nil { + certVerify := &certificateVerifyMsg{ + hasSignatureAlgorithm: c.vers >= VersionTLS12, + } + + // Determine the hash to sign. + privKey := c.config.Certificates[0].PrivateKey + + if certVerify.hasSignatureAlgorithm { + certVerify.signatureAlgorithm, err = selectSignatureAlgorithm(c.vers, privKey, c.config, certReq.signatureAlgorithms) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + } + + if c.vers > VersionSSL30 { + certVerify.signature, err = signMessage(c.vers, privKey, c.config, certVerify.signatureAlgorithm, hs.finishedHash.buffer) + if err == nil && c.config.Bugs.SendSignatureAlgorithm != 0 { + certVerify.signatureAlgorithm = c.config.Bugs.SendSignatureAlgorithm + } + } else { + // SSL 3.0's client certificate construction is + // incompatible with signatureAlgorithm. + rsaKey, ok := privKey.(*rsa.PrivateKey) + if !ok { + err = errors.New("unsupported signature type for client certificate") + } else { + digest := hs.finishedHash.hashForClientCertificateSSL3(hs.masterSecret) + if c.config.Bugs.InvalidSignature { + digest[0] ^= 0x80 + } + certVerify.signature, err = rsa.SignPKCS1v15(c.config.rand(), rsaKey, crypto.MD5SHA1, digest) + } + } + if err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to sign handshake with client certificate: " + err.Error()) + } + + hs.writeClientHash(certVerify.marshal()) + c.writeRecord(recordTypeHandshake, certVerify.marshal()) + } + // flushHandshake will be called in sendFinished. + + hs.finishedHash.discardHandshakeBuffer() + + return nil +} + +func (hs *clientHandshakeState) verifyCertificates(certMsg *certificateMsg) error { + c := hs.c + + if len(certMsg.certificates) == 0 { + c.sendAlert(alertIllegalParameter) + return errors.New("tls: no certificates sent") + } + + certs := make([]*x509.Certificate, len(certMsg.certificates)) + for i, certEntry := range certMsg.certificates { + cert, err := x509.ParseCertificate(certEntry.data) + if err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("tls: failed to parse certificate from server: " + err.Error()) + } + certs[i] = cert + } + + if !c.config.InsecureSkipVerify { + opts := x509.VerifyOptions{ + Roots: c.config.RootCAs, + CurrentTime: c.config.time(), + DNSName: c.config.ServerName, + Intermediates: x509.NewCertPool(), + } + + for i, cert := range certs { + if i == 0 { + continue + } + opts.Intermediates.AddCert(cert) + } + var err error + c.verifiedChains, err = certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + switch certs[0].PublicKey.(type) { + case *rsa.PublicKey, *ecdsa.PublicKey: + break + default: + c.sendAlert(alertUnsupportedCertificate) + return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey) + } + + c.peerCertificates = certs + return nil +} + +func (hs *clientHandshakeState) establishKeys() error { + c := hs.c + + clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := + keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen(c.vers)) + var clientCipher, serverCipher interface{} + var clientHash, serverHash macFunction + if hs.suite.cipher != nil { + clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */) + clientHash = hs.suite.mac(c.vers, clientMAC) + serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */) + serverHash = hs.suite.mac(c.vers, serverMAC) + } else { + clientCipher = hs.suite.aead(c.vers, clientKey, clientIV) + serverCipher = hs.suite.aead(c.vers, serverKey, serverIV) + } + + c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) + c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) + return nil +} + +func (hs *clientHandshakeState) processServerExtensions(serverExtensions *serverExtensions) error { + c := hs.c + + if c.vers < VersionTLS13 { + if c.config.Bugs.RequireRenegotiationInfo && serverExtensions.secureRenegotiation == nil { + return errors.New("tls: renegotiation extension missing") + } + + if len(c.clientVerify) > 0 && !c.noRenegotiationInfo() { + var expectedRenegInfo []byte + expectedRenegInfo = append(expectedRenegInfo, c.clientVerify...) + expectedRenegInfo = append(expectedRenegInfo, c.serverVerify...) + if !bytes.Equal(serverExtensions.secureRenegotiation, expectedRenegInfo) { + c.sendAlert(alertHandshakeFailure) + return fmt.Errorf("tls: renegotiation mismatch") + } + } + } else if serverExtensions.secureRenegotiation != nil { + return errors.New("tls: renegotiation info sent in TLS 1.3") + } + + if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil { + if serverExtensions.customExtension != *expected { + return fmt.Errorf("tls: bad custom extension contents %q", serverExtensions.customExtension) + } + } + + clientDidNPN := hs.hello.nextProtoNeg + clientDidALPN := len(hs.hello.alpnProtocols) > 0 + serverHasNPN := serverExtensions.nextProtoNeg + serverHasALPN := len(serverExtensions.alpnProtocol) > 0 + + if !clientDidNPN && serverHasNPN { + c.sendAlert(alertHandshakeFailure) + return errors.New("server advertised unrequested NPN extension") + } + + if !clientDidALPN && serverHasALPN { + c.sendAlert(alertHandshakeFailure) + return errors.New("server advertised unrequested ALPN extension") + } + + if serverHasNPN && serverHasALPN { + c.sendAlert(alertHandshakeFailure) + return errors.New("server advertised both NPN and ALPN extensions") + } + + if serverHasALPN { + c.clientProtocol = serverExtensions.alpnProtocol + c.clientProtocolFallback = false + c.usedALPN = true + } + + if serverHasNPN && c.vers >= VersionTLS13 { + c.sendAlert(alertHandshakeFailure) + return errors.New("server advertised NPN over TLS 1.3") + } + + if !hs.hello.channelIDSupported && serverExtensions.channelIDRequested { + c.sendAlert(alertHandshakeFailure) + return errors.New("server advertised unrequested Channel ID extension") + } + + if serverExtensions.extendedMasterSecret && c.vers >= VersionTLS13 { + return errors.New("tls: server advertised extended master secret over TLS 1.3") + } + + if serverExtensions.ticketSupported && c.vers >= VersionTLS13 { + return errors.New("tls: server advertised ticket extension over TLS 1.3") + } + + if serverExtensions.ocspStapling && c.vers >= VersionTLS13 { + return errors.New("tls: server advertised OCSP in ServerHello over TLS 1.3") + } + + if serverExtensions.ocspStapling && c.config.Bugs.NoOCSPStapling { + return errors.New("tls: server advertised unrequested OCSP extension") + } + + if len(serverExtensions.sctList) > 0 && c.vers >= VersionTLS13 { + return errors.New("tls: server advertised SCTs in ServerHello over TLS 1.3") + } + + if len(serverExtensions.sctList) > 0 && c.config.Bugs.NoSignedCertificateTimestamps { + return errors.New("tls: server advertised unrequested SCTs") + } + + if serverExtensions.srtpProtectionProfile != 0 { + if serverExtensions.srtpMasterKeyIdentifier != "" { + return errors.New("tls: server selected SRTP MKI value") + } + + found := false + for _, p := range c.config.SRTPProtectionProfiles { + if p == serverExtensions.srtpProtectionProfile { + found = true + break + } + } + if !found { + return errors.New("tls: server advertised unsupported SRTP profile") + } + + c.srtpProtectionProfile = serverExtensions.srtpProtectionProfile + } + + return nil +} + +func (hs *clientHandshakeState) serverResumedSession() bool { + // If the server responded with the same sessionId then it means the + // sessionTicket is being used to resume a TLS session. + // + // Note that, if hs.hello.sessionId is a non-nil empty array, this will + // accept an empty session ID from the server as resumption. See + // EmptyTicketSessionID. + return hs.session != nil && hs.hello.sessionId != nil && + bytes.Equal(hs.serverHello.sessionId, hs.hello.sessionId) +} + +func (hs *clientHandshakeState) processServerHello() (bool, error) { + c := hs.c + + if hs.serverResumedSession() { + // For test purposes, assert that the server never accepts the + // resumption offer on renegotiation. + if c.cipherSuite != nil && c.config.Bugs.FailIfResumeOnRenego { + return false, errors.New("tls: server resumed session on renegotiation") + } + + if hs.serverHello.extensions.sctList != nil { + return false, errors.New("tls: server sent SCT extension on session resumption") + } + + if hs.serverHello.extensions.ocspStapling { + return false, errors.New("tls: server sent OCSP extension on session resumption") + } + + // Restore masterSecret and peerCerts from previous state + hs.masterSecret = hs.session.masterSecret + c.peerCertificates = hs.session.serverCertificates + c.extendedMasterSecret = hs.session.extendedMasterSecret + c.sctList = hs.session.sctList + c.ocspResponse = hs.session.ocspResponse + hs.finishedHash.discardHandshakeBuffer() + return true, nil + } + + if hs.serverHello.extensions.sctList != nil { + c.sctList = hs.serverHello.extensions.sctList + } + + return false, nil +} + +func (hs *clientHandshakeState) readFinished(out []byte) error { + c := hs.c + + c.readRecord(recordTypeChangeCipherSpec) + if err := c.in.error(); err != nil { + return err + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + serverFinished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverFinished, msg) + } + + if c.config.Bugs.EarlyChangeCipherSpec == 0 { + verify := hs.finishedHash.serverSum(hs.masterSecret) + if len(verify) != len(serverFinished.verifyData) || + subtle.ConstantTimeCompare(verify, serverFinished.verifyData) != 1 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: server's Finished message was incorrect") + } + } + c.serverVerify = append(c.serverVerify[:0], serverFinished.verifyData...) + copy(out, serverFinished.verifyData) + hs.writeServerHash(serverFinished.marshal()) + return nil +} + +func (hs *clientHandshakeState) readSessionTicket() error { + c := hs.c + + // Create a session with no server identifier. Either a + // session ID or session ticket will be attached. + session := &ClientSessionState{ + vers: c.vers, + cipherSuite: hs.suite.id, + masterSecret: hs.masterSecret, + handshakeHash: hs.finishedHash.Sum(), + serverCertificates: c.peerCertificates, + sctList: c.sctList, + ocspResponse: c.ocspResponse, + ticketExpiration: c.config.time().Add(time.Duration(7 * 24 * time.Hour)), + } + + if !hs.serverHello.extensions.ticketSupported { + if c.config.Bugs.ExpectNewTicket { + return errors.New("tls: expected new ticket") + } + if hs.session == nil && len(hs.serverHello.sessionId) > 0 { + session.sessionId = hs.serverHello.sessionId + hs.session = session + } + return nil + } + + if c.vers == VersionSSL30 { + return errors.New("tls: negotiated session tickets in SSL 3.0") + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + sessionTicketMsg, ok := msg.(*newSessionTicketMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(sessionTicketMsg, msg) + } + + session.sessionTicket = sessionTicketMsg.ticket + hs.session = session + + hs.writeServerHash(sessionTicketMsg.marshal()) + + return nil +} + +func (hs *clientHandshakeState) sendFinished(out []byte, isResume bool) error { + c := hs.c + + var postCCSMsgs [][]byte + seqno := hs.c.sendHandshakeSeq + if hs.serverHello.extensions.nextProtoNeg { + nextProto := new(nextProtoMsg) + proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.extensions.nextProtos) + nextProto.proto = proto + c.clientProtocol = proto + c.clientProtocolFallback = fallback + + nextProtoBytes := nextProto.marshal() + hs.writeHash(nextProtoBytes, seqno) + seqno++ + postCCSMsgs = append(postCCSMsgs, nextProtoBytes) + } + + if hs.serverHello.extensions.channelIDRequested { + var resumeHash []byte + if isResume { + resumeHash = hs.session.handshakeHash + } + channelIDMsgBytes, err := hs.writeChannelIDMessage(hs.finishedHash.hashForChannelID(resumeHash)) + if err != nil { + return err + } + hs.writeHash(channelIDMsgBytes, seqno) + seqno++ + postCCSMsgs = append(postCCSMsgs, channelIDMsgBytes) + } + + finished := new(finishedMsg) + if c.config.Bugs.EarlyChangeCipherSpec == 2 { + finished.verifyData = hs.finishedHash.clientSum(nil) + } else { + finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret) + } + copy(out, finished.verifyData) + if c.config.Bugs.BadFinished { + finished.verifyData[0]++ + } + c.clientVerify = append(c.clientVerify[:0], finished.verifyData...) + hs.finishedBytes = finished.marshal() + hs.writeHash(hs.finishedBytes, seqno) + postCCSMsgs = append(postCCSMsgs, hs.finishedBytes) + + if c.config.Bugs.FragmentAcrossChangeCipherSpec { + c.writeRecord(recordTypeHandshake, postCCSMsgs[0][:5]) + postCCSMsgs[0] = postCCSMsgs[0][5:] + } else if c.config.Bugs.SendUnencryptedFinished { + c.writeRecord(recordTypeHandshake, postCCSMsgs[0]) + postCCSMsgs = postCCSMsgs[1:] + } + c.flushHandshake() + + if !c.config.Bugs.SkipChangeCipherSpec && + c.config.Bugs.EarlyChangeCipherSpec == 0 { + ccs := []byte{1} + if c.config.Bugs.BadChangeCipherSpec != nil { + ccs = c.config.Bugs.BadChangeCipherSpec + } + c.writeRecord(recordTypeChangeCipherSpec, ccs) + } + + if c.config.Bugs.AppDataAfterChangeCipherSpec != nil { + c.writeRecord(recordTypeApplicationData, c.config.Bugs.AppDataAfterChangeCipherSpec) + } + if c.config.Bugs.AlertAfterChangeCipherSpec != 0 { + c.sendAlert(c.config.Bugs.AlertAfterChangeCipherSpec) + return errors.New("tls: simulating post-CCS alert") + } + + if !c.config.Bugs.SkipFinished { + for _, msg := range postCCSMsgs { + c.writeRecord(recordTypeHandshake, msg) + } + + if c.config.Bugs.SendExtraFinished { + c.writeRecord(recordTypeHandshake, finished.marshal()) + } + + c.flushHandshake() + } + return nil +} + +func (hs *clientHandshakeState) writeChannelIDMessage(channelIDHash []byte) ([]byte, error) { + c := hs.c + channelIDMsg := new(channelIDMsg) + if c.config.ChannelID.Curve != elliptic.P256() { + return nil, fmt.Errorf("tls: Channel ID is not on P-256.") + } + r, s, err := ecdsa.Sign(c.config.rand(), c.config.ChannelID, channelIDHash) + if err != nil { + return nil, err + } + channelID := make([]byte, 128) + writeIntPadded(channelID[0:32], c.config.ChannelID.X) + writeIntPadded(channelID[32:64], c.config.ChannelID.Y) + writeIntPadded(channelID[64:96], r) + writeIntPadded(channelID[96:128], s) + if c.config.Bugs.InvalidChannelIDSignature { + channelID[64] ^= 1 + } + channelIDMsg.channelID = channelID + + c.channelID = &c.config.ChannelID.PublicKey + + return channelIDMsg.marshal(), nil +} + +func (hs *clientHandshakeState) writeClientHash(msg []byte) { + // writeClientHash is called before writeRecord. + hs.writeHash(msg, hs.c.sendHandshakeSeq) +} + +func (hs *clientHandshakeState) writeServerHash(msg []byte) { + // writeServerHash is called after readHandshake. + hs.writeHash(msg, hs.c.recvHandshakeSeq-1) +} + +func (hs *clientHandshakeState) writeHash(msg []byte, seqno uint16) { + if hs.c.isDTLS { + // This is somewhat hacky. DTLS hashes a slightly different format. + // First, the TLS header. + hs.finishedHash.Write(msg[:4]) + // Then the sequence number and reassembled fragment offset (always 0). + hs.finishedHash.Write([]byte{byte(seqno >> 8), byte(seqno), 0, 0, 0}) + // Then the reassembled fragment (always equal to the message length). + hs.finishedHash.Write(msg[1:4]) + // And then the message body. + hs.finishedHash.Write(msg[4:]) + } else { + hs.finishedHash.Write(msg) + } +} + +// selectClientCertificate selects a certificate for use with the given +// certificate, or none if none match. It may return a particular certificate or +// nil on success, or an error on internal error. +func selectClientCertificate(c *Conn, certReq *certificateRequestMsg) (*Certificate, error) { + // RFC 4346 on the certificateAuthorities field: + // A list of the distinguished names of acceptable certificate + // authorities. These distinguished names may specify a desired + // distinguished name for a root CA or for a subordinate CA; thus, this + // message can be used to describe both known roots and a desired + // authorization space. If the certificate_authorities list is empty + // then the client MAY send any certificate of the appropriate + // ClientCertificateType, unless there is some external arrangement to + // the contrary. + + var rsaAvail, ecdsaAvail bool + if !certReq.hasRequestContext { + for _, certType := range certReq.certificateTypes { + switch certType { + case CertTypeRSASign: + rsaAvail = true + case CertTypeECDSASign: + ecdsaAvail = true + } + } + } + + // We need to search our list of client certs for one + // where SignatureAlgorithm is RSA and the Issuer is in + // certReq.certificateAuthorities +findCert: + for i, chain := range c.config.Certificates { + if !certReq.hasRequestContext && !rsaAvail && !ecdsaAvail { + continue + } + + // Ensure the private key supports one of the advertised + // signature algorithms. + if certReq.hasSignatureAlgorithm { + if _, err := selectSignatureAlgorithm(c.vers, chain.PrivateKey, c.config, certReq.signatureAlgorithms); err != nil { + continue + } + } + + for j, cert := range chain.Certificate { + x509Cert := chain.Leaf + // parse the certificate if this isn't the leaf + // node, or if chain.Leaf was nil + if j != 0 || x509Cert == nil { + var err error + if x509Cert, err = x509.ParseCertificate(cert); err != nil { + c.sendAlert(alertInternalError) + return nil, errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + } + } + + if !certReq.hasRequestContext { + switch { + case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: + case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: + default: + continue findCert + } + } + + if expected := c.config.Bugs.ExpectCertificateReqNames; expected != nil { + if !eqByteSlices(expected, certReq.certificateAuthorities) { + return nil, fmt.Errorf("tls: CertificateRequest names differed, got %#v but expected %#v", certReq.certificateAuthorities, expected) + } + } + + return &chain, nil + } + } + + return nil, nil +} + +// clientSessionCacheKey returns a key used to cache sessionTickets that could +// be used to resume previously negotiated TLS sessions with a server. +func clientSessionCacheKey(serverAddr net.Addr, config *Config) string { + if len(config.ServerName) > 0 { + return config.ServerName + } + return serverAddr.String() +} + +// mutualProtocol finds the mutual Next Protocol Negotiation or ALPN protocol +// given list of possible protocols and a list of the preference order. The +// first list must not be empty. It returns the resulting protocol and flag +// indicating if the fallback case was reached. +func mutualProtocol(protos, preferenceProtos []string) (string, bool) { + for _, s := range preferenceProtos { + for _, c := range protos { + if s == c { + return s, false + } + } + } + + return protos[0], true +} + +// writeIntPadded writes x into b, padded up with leading zeros as +// needed. +func writeIntPadded(b []byte, x *big.Int) { + for i := range b { + b[i] = 0 + } + xb := x.Bytes() + copy(b[len(b)-len(xb):], xb) +} + +func generatePSKBinders(hello *clientHelloMsg, pskCipherSuite *cipherSuite, psk, transcript []byte, config *Config) { + if config.Bugs.SendNoPSKBinder { + return + } + + binderLen := pskCipherSuite.hash().Size() + if config.Bugs.SendShortPSKBinder { + binderLen-- + } + + numBinders := 1 + if config.Bugs.SendExtraPSKBinder { + numBinders++ + } + + // Fill hello.pskBinders with appropriate length arrays of zeros so the + // length prefixes are correct when computing the binder over the truncated + // ClientHello message. + hello.pskBinders = make([][]byte, numBinders) + for i := range hello.pskBinders { + hello.pskBinders[i] = make([]byte, binderLen) + } + + helloBytes := hello.marshal() + binderSize := len(hello.pskBinders)*(binderLen+1) + 2 + truncatedHello := helloBytes[:len(helloBytes)-binderSize] + binder := computePSKBinder(psk, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello) + if config.Bugs.SendShortPSKBinder { + binder = binder[:binderLen] + } + if config.Bugs.SendInvalidPSKBinder { + binder[0] ^= 1 + } + + for i := range hello.pskBinders { + hello.pskBinders[i] = binder + } + + hello.raw = nil +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_messages.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_messages.go new file mode 100644 index 000000000..86e2821d5 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_messages.go @@ -0,0 +1,2369 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "bytes" + "encoding/binary" +) + +func writeLen(buf []byte, v, size int) { + for i := 0; i < size; i++ { + buf[size-i-1] = byte(v) + v >>= 8 + } + if v != 0 { + panic("length is too long") + } +} + +type byteBuilder struct { + buf *[]byte + start int + prefixLen int + child *byteBuilder +} + +func newByteBuilder() *byteBuilder { + buf := make([]byte, 0, 32) + return &byteBuilder{buf: &buf} +} + +func (bb *byteBuilder) len() int { + return len(*bb.buf) - bb.start - bb.prefixLen +} + +func (bb *byteBuilder) flush() { + if bb.child == nil { + return + } + bb.child.flush() + writeLen((*bb.buf)[bb.child.start:], bb.child.len(), bb.child.prefixLen) + bb.child = nil + return +} + +func (bb *byteBuilder) finish() []byte { + bb.flush() + return *bb.buf +} + +func (bb *byteBuilder) addU8(u uint8) { + bb.flush() + *bb.buf = append(*bb.buf, u) +} + +func (bb *byteBuilder) addU16(u uint16) { + bb.flush() + *bb.buf = append(*bb.buf, byte(u>>8), byte(u)) +} + +func (bb *byteBuilder) addU24(u int) { + bb.flush() + *bb.buf = append(*bb.buf, byte(u>>16), byte(u>>8), byte(u)) +} + +func (bb *byteBuilder) addU32(u uint32) { + bb.flush() + *bb.buf = append(*bb.buf, byte(u>>24), byte(u>>16), byte(u>>8), byte(u)) +} + +func (bb *byteBuilder) addU64(u uint64) { + bb.flush() + var b [8]byte + binary.BigEndian.PutUint64(b[:], u) + *bb.buf = append(*bb.buf, b[:]...) +} + +func (bb *byteBuilder) addU8LengthPrefixed() *byteBuilder { + return bb.createChild(1) +} + +func (bb *byteBuilder) addU16LengthPrefixed() *byteBuilder { + return bb.createChild(2) +} + +func (bb *byteBuilder) addU24LengthPrefixed() *byteBuilder { + return bb.createChild(3) +} + +func (bb *byteBuilder) addU32LengthPrefixed() *byteBuilder { + return bb.createChild(4) +} + +func (bb *byteBuilder) addBytes(b []byte) { + bb.flush() + *bb.buf = append(*bb.buf, b...) +} + +func (bb *byteBuilder) createChild(lengthPrefixSize int) *byteBuilder { + bb.flush() + bb.child = &byteBuilder{ + buf: bb.buf, + start: len(*bb.buf), + prefixLen: lengthPrefixSize, + } + for i := 0; i < lengthPrefixSize; i++ { + *bb.buf = append(*bb.buf, 0) + } + return bb.child +} + +func (bb *byteBuilder) discardChild() { + if bb.child != nil { + return + } + bb.child = nil + *bb.buf = (*bb.buf)[:bb.start] +} + +type keyShareEntry struct { + group CurveID + keyExchange []byte +} + +type pskIdentity struct { + ticket []uint8 + obfuscatedTicketAge uint32 +} + +type clientHelloMsg struct { + raw []byte + isDTLS bool + vers uint16 + random []byte + sessionId []byte + cookie []byte + cipherSuites []uint16 + compressionMethods []uint8 + nextProtoNeg bool + serverName string + ocspStapling bool + supportedCurves []CurveID + supportedPoints []uint8 + hasKeyShares bool + keyShares []keyShareEntry + trailingKeyShareData bool + pskIdentities []pskIdentity + pskKEModes []byte + pskBinders [][]uint8 + hasEarlyData bool + tls13Cookie []byte + ticketSupported bool + sessionTicket []uint8 + signatureAlgorithms []signatureAlgorithm + supportedVersions []uint16 + secureRenegotiation []byte + alpnProtocols []string + duplicateExtension bool + channelIDSupported bool + npnAfterAlpn bool + extendedMasterSecret bool + srtpProtectionProfiles []uint16 + srtpMasterKeyIdentifier string + sctListSupported bool + customExtension string + hasGREASEExtension bool + pskBinderFirst bool +} + +func (m *clientHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*clientHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.isDTLS == m1.isDTLS && + m.vers == m1.vers && + bytes.Equal(m.random, m1.random) && + bytes.Equal(m.sessionId, m1.sessionId) && + bytes.Equal(m.cookie, m1.cookie) && + eqUint16s(m.cipherSuites, m1.cipherSuites) && + bytes.Equal(m.compressionMethods, m1.compressionMethods) && + m.nextProtoNeg == m1.nextProtoNeg && + m.serverName == m1.serverName && + m.ocspStapling == m1.ocspStapling && + eqCurveIDs(m.supportedCurves, m1.supportedCurves) && + bytes.Equal(m.supportedPoints, m1.supportedPoints) && + m.hasKeyShares == m1.hasKeyShares && + eqKeyShareEntryLists(m.keyShares, m1.keyShares) && + m.trailingKeyShareData == m1.trailingKeyShareData && + eqPSKIdentityLists(m.pskIdentities, m1.pskIdentities) && + bytes.Equal(m.pskKEModes, m1.pskKEModes) && + eqByteSlices(m.pskBinders, m1.pskBinders) && + m.hasEarlyData == m1.hasEarlyData && + bytes.Equal(m.tls13Cookie, m1.tls13Cookie) && + m.ticketSupported == m1.ticketSupported && + bytes.Equal(m.sessionTicket, m1.sessionTicket) && + eqSignatureAlgorithms(m.signatureAlgorithms, m1.signatureAlgorithms) && + eqUint16s(m.supportedVersions, m1.supportedVersions) && + bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && + (m.secureRenegotiation == nil) == (m1.secureRenegotiation == nil) && + eqStrings(m.alpnProtocols, m1.alpnProtocols) && + m.duplicateExtension == m1.duplicateExtension && + m.channelIDSupported == m1.channelIDSupported && + m.npnAfterAlpn == m1.npnAfterAlpn && + m.extendedMasterSecret == m1.extendedMasterSecret && + eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) && + m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier && + m.sctListSupported == m1.sctListSupported && + m.customExtension == m1.customExtension && + m.hasGREASEExtension == m1.hasGREASEExtension && + m.pskBinderFirst == m1.pskBinderFirst +} + +func (m *clientHelloMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + handshakeMsg := newByteBuilder() + handshakeMsg.addU8(typeClientHello) + hello := handshakeMsg.addU24LengthPrefixed() + hello.addU16(m.vers) + hello.addBytes(m.random) + sessionId := hello.addU8LengthPrefixed() + sessionId.addBytes(m.sessionId) + if m.isDTLS { + cookie := hello.addU8LengthPrefixed() + cookie.addBytes(m.cookie) + } + cipherSuites := hello.addU16LengthPrefixed() + for _, suite := range m.cipherSuites { + cipherSuites.addU16(suite) + } + compressionMethods := hello.addU8LengthPrefixed() + compressionMethods.addBytes(m.compressionMethods) + + extensions := hello.addU16LengthPrefixed() + if len(m.pskIdentities) > 0 && m.pskBinderFirst { + extensions.addU16(extensionPreSharedKey) + pskExtension := extensions.addU16LengthPrefixed() + + pskIdentities := pskExtension.addU16LengthPrefixed() + for _, psk := range m.pskIdentities { + pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket) + pskIdentities.addU32(psk.obfuscatedTicketAge) + } + pskBinders := pskExtension.addU16LengthPrefixed() + for _, binder := range m.pskBinders { + pskBinders.addU8LengthPrefixed().addBytes(binder) + } + } + if m.duplicateExtension { + // Add a duplicate bogus extension at the beginning and end. + extensions.addU16(0xffff) + extensions.addU16(0) // 0-length for empty extension + } + if m.nextProtoNeg && !m.npnAfterAlpn { + extensions.addU16(extensionNextProtoNeg) + extensions.addU16(0) // The length is always 0 + } + if len(m.serverName) > 0 { + extensions.addU16(extensionServerName) + serverNameList := extensions.addU16LengthPrefixed() + + // RFC 3546, section 3.1 + // + // struct { + // NameType name_type; + // select (name_type) { + // case host_name: HostName; + // } name; + // } ServerName; + // + // enum { + // host_name(0), (255) + // } NameType; + // + // opaque HostName<1..2^16-1>; + // + // struct { + // ServerName server_name_list<1..2^16-1> + // } ServerNameList; + + serverName := serverNameList.addU16LengthPrefixed() + serverName.addU8(0) // NameType host_name(0) + hostName := serverName.addU16LengthPrefixed() + hostName.addBytes([]byte(m.serverName)) + } + if m.ocspStapling { + extensions.addU16(extensionStatusRequest) + certificateStatusRequest := extensions.addU16LengthPrefixed() + + // RFC 4366, section 3.6 + certificateStatusRequest.addU8(1) // OCSP type + // Two zero valued uint16s for the two lengths. + certificateStatusRequest.addU16(0) // ResponderID length + certificateStatusRequest.addU16(0) // Extensions length + } + if len(m.supportedCurves) > 0 { + // http://tools.ietf.org/html/rfc4492#section-5.1.1 + extensions.addU16(extensionSupportedCurves) + supportedCurvesList := extensions.addU16LengthPrefixed() + supportedCurves := supportedCurvesList.addU16LengthPrefixed() + for _, curve := range m.supportedCurves { + supportedCurves.addU16(uint16(curve)) + } + } + if len(m.supportedPoints) > 0 { + // http://tools.ietf.org/html/rfc4492#section-5.1.2 + extensions.addU16(extensionSupportedPoints) + supportedPointsList := extensions.addU16LengthPrefixed() + supportedPoints := supportedPointsList.addU8LengthPrefixed() + supportedPoints.addBytes(m.supportedPoints) + } + if m.hasKeyShares { + extensions.addU16(extensionKeyShare) + keyShareList := extensions.addU16LengthPrefixed() + + keyShares := keyShareList.addU16LengthPrefixed() + for _, keyShare := range m.keyShares { + keyShares.addU16(uint16(keyShare.group)) + keyExchange := keyShares.addU16LengthPrefixed() + keyExchange.addBytes(keyShare.keyExchange) + } + + if m.trailingKeyShareData { + keyShares.addU8(0) + } + } + if len(m.pskKEModes) > 0 { + extensions.addU16(extensionPSKKeyExchangeModes) + pskModesExtension := extensions.addU16LengthPrefixed() + pskModesExtension.addU8LengthPrefixed().addBytes(m.pskKEModes) + } + if m.hasEarlyData { + extensions.addU16(extensionEarlyData) + extensions.addU16(0) // The length is zero. + } + if len(m.tls13Cookie) > 0 { + extensions.addU16(extensionCookie) + body := extensions.addU16LengthPrefixed() + body.addU16LengthPrefixed().addBytes(m.tls13Cookie) + } + if m.ticketSupported { + // http://tools.ietf.org/html/rfc5077#section-3.2 + extensions.addU16(extensionSessionTicket) + sessionTicketExtension := extensions.addU16LengthPrefixed() + sessionTicketExtension.addBytes(m.sessionTicket) + } + if len(m.signatureAlgorithms) > 0 { + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + extensions.addU16(extensionSignatureAlgorithms) + signatureAlgorithmsExtension := extensions.addU16LengthPrefixed() + signatureAlgorithms := signatureAlgorithmsExtension.addU16LengthPrefixed() + for _, sigAlg := range m.signatureAlgorithms { + signatureAlgorithms.addU16(uint16(sigAlg)) + } + } + if len(m.supportedVersions) > 0 { + extensions.addU16(extensionSupportedVersions) + supportedVersionsExtension := extensions.addU16LengthPrefixed() + supportedVersions := supportedVersionsExtension.addU8LengthPrefixed() + for _, version := range m.supportedVersions { + supportedVersions.addU16(uint16(version)) + } + } + if m.secureRenegotiation != nil { + extensions.addU16(extensionRenegotiationInfo) + secureRenegoExt := extensions.addU16LengthPrefixed() + secureRenego := secureRenegoExt.addU8LengthPrefixed() + secureRenego.addBytes(m.secureRenegotiation) + } + if len(m.alpnProtocols) > 0 { + // https://tools.ietf.org/html/rfc7301#section-3.1 + extensions.addU16(extensionALPN) + alpnExtension := extensions.addU16LengthPrefixed() + + protocolNameList := alpnExtension.addU16LengthPrefixed() + for _, s := range m.alpnProtocols { + protocolName := protocolNameList.addU8LengthPrefixed() + protocolName.addBytes([]byte(s)) + } + } + if m.channelIDSupported { + extensions.addU16(extensionChannelID) + extensions.addU16(0) // Length is always 0 + } + if m.nextProtoNeg && m.npnAfterAlpn { + extensions.addU16(extensionNextProtoNeg) + extensions.addU16(0) // Length is always 0 + } + if m.duplicateExtension { + // Add a duplicate bogus extension at the beginning and end. + extensions.addU16(0xffff) + extensions.addU16(0) + } + if m.extendedMasterSecret { + // https://tools.ietf.org/html/rfc7627 + extensions.addU16(extensionExtendedMasterSecret) + extensions.addU16(0) // Length is always 0 + } + if len(m.srtpProtectionProfiles) > 0 { + // https://tools.ietf.org/html/rfc5764#section-4.1.1 + extensions.addU16(extensionUseSRTP) + useSrtpExt := extensions.addU16LengthPrefixed() + + srtpProtectionProfiles := useSrtpExt.addU16LengthPrefixed() + for _, p := range m.srtpProtectionProfiles { + // An SRTPProtectionProfile is defined as uint8[2], + // not uint16. For some reason, we're storing it + // as a uint16. + srtpProtectionProfiles.addU8(byte(p >> 8)) + srtpProtectionProfiles.addU8(byte(p)) + } + srtpMki := useSrtpExt.addU8LengthPrefixed() + srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier)) + } + if m.sctListSupported { + extensions.addU16(extensionSignedCertificateTimestamp) + extensions.addU16(0) // Length is always 0 + } + if l := len(m.customExtension); l > 0 { + extensions.addU16(extensionCustom) + customExt := extensions.addU16LengthPrefixed() + customExt.addBytes([]byte(m.customExtension)) + } + // The PSK extension must be last (draft-ietf-tls-tls13-18 section 4.2.6). + if len(m.pskIdentities) > 0 && !m.pskBinderFirst { + extensions.addU16(extensionPreSharedKey) + pskExtension := extensions.addU16LengthPrefixed() + + pskIdentities := pskExtension.addU16LengthPrefixed() + for _, psk := range m.pskIdentities { + pskIdentities.addU16LengthPrefixed().addBytes(psk.ticket) + pskIdentities.addU32(psk.obfuscatedTicketAge) + } + pskBinders := pskExtension.addU16LengthPrefixed() + for _, binder := range m.pskBinders { + pskBinders.addU8LengthPrefixed().addBytes(binder) + } + } + + if extensions.len() == 0 { + hello.discardChild() + } + + m.raw = handshakeMsg.finish() + return m.raw +} + +func (m *clientHelloMsg) unmarshal(data []byte) bool { + if len(data) < 42 { + return false + } + m.raw = data + m.vers = uint16(data[4])<<8 | uint16(data[5]) + m.random = data[6:38] + sessionIdLen := int(data[38]) + if sessionIdLen > 32 || len(data) < 39+sessionIdLen { + return false + } + m.sessionId = data[39 : 39+sessionIdLen] + data = data[39+sessionIdLen:] + if m.isDTLS { + if len(data) < 1 { + return false + } + cookieLen := int(data[0]) + if cookieLen > 32 || len(data) < 1+cookieLen { + return false + } + m.cookie = data[1 : 1+cookieLen] + data = data[1+cookieLen:] + } + if len(data) < 2 { + return false + } + // cipherSuiteLen is the number of bytes of cipher suite numbers. Since + // they are uint16s, the number must be even. + cipherSuiteLen := int(data[0])<<8 | int(data[1]) + if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen { + return false + } + numCipherSuites := cipherSuiteLen / 2 + m.cipherSuites = make([]uint16, numCipherSuites) + for i := 0; i < numCipherSuites; i++ { + m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i]) + if m.cipherSuites[i] == scsvRenegotiation { + m.secureRenegotiation = []byte{} + } + } + data = data[2+cipherSuiteLen:] + if len(data) < 1 { + return false + } + compressionMethodsLen := int(data[0]) + if len(data) < 1+compressionMethodsLen { + return false + } + m.compressionMethods = data[1 : 1+compressionMethodsLen] + + data = data[1+compressionMethodsLen:] + + m.nextProtoNeg = false + m.serverName = "" + m.ocspStapling = false + m.keyShares = nil + m.pskIdentities = nil + m.hasEarlyData = false + m.ticketSupported = false + m.sessionTicket = nil + m.signatureAlgorithms = nil + m.supportedVersions = nil + m.alpnProtocols = nil + m.extendedMasterSecret = false + m.customExtension = "" + + if len(data) == 0 { + // ClientHello is optionally followed by extension data + return true + } + if len(data) < 2 { + return false + } + + extensionsLength := int(data[0])<<8 | int(data[1]) + data = data[2:] + if extensionsLength != len(data) { + return false + } + + for len(data) != 0 { + if len(data) < 4 { + return false + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) < length { + return false + } + + switch extension { + case extensionServerName: + if length < 2 { + return false + } + numNames := int(data[0])<<8 | int(data[1]) + d := data[2:] + for i := 0; i < numNames; i++ { + if len(d) < 3 { + return false + } + nameType := d[0] + nameLen := int(d[1])<<8 | int(d[2]) + d = d[3:] + if len(d) < nameLen { + return false + } + if nameType == 0 { + m.serverName = string(d[0:nameLen]) + break + } + d = d[nameLen:] + } + case extensionNextProtoNeg: + if length > 0 { + return false + } + m.nextProtoNeg = true + case extensionStatusRequest: + m.ocspStapling = length > 0 && data[0] == statusTypeOCSP + case extensionSupportedCurves: + // http://tools.ietf.org/html/rfc4492#section-5.5.1 + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l%2 == 1 || length != l+2 { + return false + } + numCurves := l / 2 + m.supportedCurves = make([]CurveID, numCurves) + d := data[2:] + for i := 0; i < numCurves; i++ { + m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1]) + d = d[2:] + } + case extensionSupportedPoints: + // http://tools.ietf.org/html/rfc4492#section-5.5.2 + if length < 1 { + return false + } + l := int(data[0]) + if length != l+1 { + return false + } + m.supportedPoints = data[1 : 1+l] + case extensionSessionTicket: + // http://tools.ietf.org/html/rfc5077#section-3.2 + m.ticketSupported = true + m.sessionTicket = data[:length] + case extensionKeyShare: + // draft-ietf-tls-tls13 section 6.3.2.3 + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l != length-2 { + return false + } + d := data[2:length] + m.hasKeyShares = true + for len(d) > 0 { + // The next KeyShareEntry contains a NamedGroup (2 bytes) and a + // key_exchange (2-byte length prefix with at least 1 byte of content). + if len(d) < 5 { + return false + } + entry := keyShareEntry{} + entry.group = CurveID(d[0])<<8 | CurveID(d[1]) + keyExchLen := int(d[2])<<8 | int(d[3]) + d = d[4:] + if len(d) < keyExchLen { + return false + } + entry.keyExchange = d[:keyExchLen] + d = d[keyExchLen:] + m.keyShares = append(m.keyShares, entry) + } + case extensionPreSharedKey: + // draft-ietf-tls-tls13-18 section 4.2.6 + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + d := data[2 : l+2] + // Parse PSK identities. + for len(d) > 0 { + if len(d) < 2 { + return false + } + pskLen := int(d[0])<<8 | int(d[1]) + d = d[2:] + + if len(d) < pskLen+4 { + return false + } + ticket := d[:pskLen] + obfuscatedTicketAge := uint32(d[pskLen])<<24 | uint32(d[pskLen+1])<<16 | uint32(d[pskLen+2])<<8 | uint32(d[pskLen+3]) + psk := pskIdentity{ + ticket: ticket, + obfuscatedTicketAge: obfuscatedTicketAge, + } + m.pskIdentities = append(m.pskIdentities, psk) + d = d[pskLen+4:] + } + d = data[l+2:] + if len(d) < 2 { + return false + } + l = int(d[0])<<8 | int(d[1]) + d = d[2:] + if l != len(d) { + return false + } + // Parse PSK binders. + for len(d) > 0 { + if len(d) < 1 { + return false + } + binderLen := int(d[0]) + d = d[1:] + if binderLen > len(d) { + return false + } + m.pskBinders = append(m.pskBinders, d[:binderLen]) + d = d[binderLen:] + } + + // There must be the same number of identities as binders. + if len(m.pskIdentities) != len(m.pskBinders) { + return false + } + case extensionPSKKeyExchangeModes: + // draft-ietf-tls-tls13-18 section 4.2.7 + if length < 1 { + return false + } + l := int(data[0]) + if l != length-1 { + return false + } + m.pskKEModes = data[1:length] + case extensionEarlyData: + // draft-ietf-tls-tls13 section 6.3.2.5 + if length != 0 { + return false + } + m.hasEarlyData = true + case extensionCookie: + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l != length-2 || l == 0 { + return false + } + m.tls13Cookie = data[2 : 2+l] + case extensionSignatureAlgorithms: + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + if length < 2 || length&1 != 0 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l != length-2 { + return false + } + n := l / 2 + d := data[2:] + m.signatureAlgorithms = make([]signatureAlgorithm, n) + for i := range m.signatureAlgorithms { + m.signatureAlgorithms[i] = signatureAlgorithm(d[0])<<8 | signatureAlgorithm(d[1]) + d = d[2:] + } + case extensionSupportedVersions: + if length < 1+2 { + return false + } + l := int(data[0]) + if l != length-1 || l%2 == 1 || l < 2 { + return false + } + n := l / 2 + d := data[1:] + m.supportedVersions = make([]uint16, n) + for i := range m.supportedVersions { + m.supportedVersions[i] = uint16(d[0])<<8 | uint16(d[1]) + d = d[2:] + } + case extensionRenegotiationInfo: + if length < 1 || length != int(data[0])+1 { + return false + } + m.secureRenegotiation = data[1:length] + case extensionALPN: + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l != length-2 { + return false + } + d := data[2:length] + for len(d) != 0 { + stringLen := int(d[0]) + d = d[1:] + if stringLen == 0 || stringLen > len(d) { + return false + } + m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen])) + d = d[stringLen:] + } + case extensionChannelID: + if length > 0 { + return false + } + m.channelIDSupported = true + case extensionExtendedMasterSecret: + if length != 0 { + return false + } + m.extendedMasterSecret = true + case extensionUseSRTP: + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l > length-2 || l%2 != 0 { + return false + } + n := l / 2 + m.srtpProtectionProfiles = make([]uint16, n) + d := data[2:length] + for i := 0; i < n; i++ { + m.srtpProtectionProfiles[i] = uint16(d[0])<<8 | uint16(d[1]) + d = d[2:] + } + if len(d) < 1 || int(d[0]) != len(d)-1 { + return false + } + m.srtpMasterKeyIdentifier = string(d[1:]) + case extensionSignedCertificateTimestamp: + if length != 0 { + return false + } + m.sctListSupported = true + case extensionCustom: + m.customExtension = string(data[:length]) + } + data = data[length:] + + if isGREASEValue(extension) { + m.hasGREASEExtension = true + } + } + + return true +} + +type serverHelloMsg struct { + raw []byte + isDTLS bool + vers uint16 + versOverride uint16 + random []byte + sessionId []byte + cipherSuite uint16 + hasKeyShare bool + keyShare keyShareEntry + hasPSKIdentity bool + pskIdentity uint16 + compressionMethod uint8 + customExtension string + unencryptedALPN string + extensions serverExtensions +} + +func (m *serverHelloMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + handshakeMsg := newByteBuilder() + handshakeMsg.addU8(typeServerHello) + hello := handshakeMsg.addU24LengthPrefixed() + + // m.vers is used both to determine the format of the rest of the + // ServerHello and to override the value, so include a second version + // field. + vers, ok := wireToVersion(m.vers, m.isDTLS) + if !ok { + panic("unknown version") + } + if m.versOverride != 0 { + hello.addU16(m.versOverride) + } else { + hello.addU16(m.vers) + } + + hello.addBytes(m.random) + if vers < VersionTLS13 { + sessionId := hello.addU8LengthPrefixed() + sessionId.addBytes(m.sessionId) + } + hello.addU16(m.cipherSuite) + if vers < VersionTLS13 { + hello.addU8(m.compressionMethod) + } + + extensions := hello.addU16LengthPrefixed() + + if vers >= VersionTLS13 { + if m.hasKeyShare { + extensions.addU16(extensionKeyShare) + keyShare := extensions.addU16LengthPrefixed() + keyShare.addU16(uint16(m.keyShare.group)) + keyExchange := keyShare.addU16LengthPrefixed() + keyExchange.addBytes(m.keyShare.keyExchange) + } + if m.hasPSKIdentity { + extensions.addU16(extensionPreSharedKey) + extensions.addU16(2) // Length + extensions.addU16(m.pskIdentity) + } + if len(m.customExtension) > 0 { + extensions.addU16(extensionCustom) + customExt := extensions.addU16LengthPrefixed() + customExt.addBytes([]byte(m.customExtension)) + } + if len(m.unencryptedALPN) > 0 { + extensions.addU16(extensionALPN) + extension := extensions.addU16LengthPrefixed() + + protocolNameList := extension.addU16LengthPrefixed() + protocolName := protocolNameList.addU8LengthPrefixed() + protocolName.addBytes([]byte(m.unencryptedALPN)) + } + } else { + m.extensions.marshal(extensions) + if extensions.len() == 0 { + hello.discardChild() + } + } + + m.raw = handshakeMsg.finish() + return m.raw +} + +func (m *serverHelloMsg) unmarshal(data []byte) bool { + if len(data) < 42 { + return false + } + m.raw = data + m.vers = uint16(data[4])<<8 | uint16(data[5]) + vers, ok := wireToVersion(m.vers, m.isDTLS) + if !ok { + return false + } + m.random = data[6:38] + data = data[38:] + if vers < VersionTLS13 { + sessionIdLen := int(data[0]) + if sessionIdLen > 32 || len(data) < 1+sessionIdLen { + return false + } + m.sessionId = data[1 : 1+sessionIdLen] + data = data[1+sessionIdLen:] + } + if len(data) < 2 { + return false + } + m.cipherSuite = uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + if vers < VersionTLS13 { + if len(data) < 1 { + return false + } + m.compressionMethod = data[0] + data = data[1:] + } + + if len(data) == 0 && m.vers < VersionTLS13 { + // Extension data is optional before TLS 1.3. + m.extensions = serverExtensions{} + return true + } + if len(data) < 2 { + return false + } + + extensionsLength := int(data[0])<<8 | int(data[1]) + data = data[2:] + if len(data) != extensionsLength { + return false + } + + if vers >= VersionTLS13 { + for len(data) != 0 { + if len(data) < 4 { + return false + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + + if len(data) < length { + return false + } + d := data[:length] + data = data[length:] + + switch extension { + case extensionKeyShare: + m.hasKeyShare = true + if len(d) < 4 { + return false + } + m.keyShare.group = CurveID(uint16(d[0])<<8 | uint16(d[1])) + keyExchLen := int(d[2])<<8 | int(d[3]) + if keyExchLen != len(d)-4 { + return false + } + m.keyShare.keyExchange = make([]byte, keyExchLen) + copy(m.keyShare.keyExchange, d[4:]) + case extensionPreSharedKey: + if len(d) != 2 { + return false + } + m.pskIdentity = uint16(d[0])<<8 | uint16(d[1]) + m.hasPSKIdentity = true + default: + // Only allow the 3 extensions that are sent in + // the clear in TLS 1.3. + return false + } + } + } else if !m.extensions.unmarshal(data, vers) { + return false + } + + return true +} + +type encryptedExtensionsMsg struct { + raw []byte + extensions serverExtensions + empty bool +} + +func (m *encryptedExtensionsMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + encryptedExtensionsMsg := newByteBuilder() + encryptedExtensionsMsg.addU8(typeEncryptedExtensions) + encryptedExtensions := encryptedExtensionsMsg.addU24LengthPrefixed() + if !m.empty { + extensions := encryptedExtensions.addU16LengthPrefixed() + m.extensions.marshal(extensions) + } + + m.raw = encryptedExtensionsMsg.finish() + return m.raw +} + +func (m *encryptedExtensionsMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 6 { + return false + } + if data[0] != typeEncryptedExtensions { + return false + } + msgLen := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) != msgLen { + return false + } + extLen := int(data[0])<<8 | int(data[1]) + data = data[2:] + if extLen != len(data) { + return false + } + return m.extensions.unmarshal(data, VersionTLS13) +} + +type serverExtensions struct { + nextProtoNeg bool + nextProtos []string + ocspStapling bool + ticketSupported bool + secureRenegotiation []byte + alpnProtocol string + alpnProtocolEmpty bool + duplicateExtension bool + channelIDRequested bool + extendedMasterSecret bool + srtpProtectionProfile uint16 + srtpMasterKeyIdentifier string + sctList []byte + customExtension string + npnAfterAlpn bool + hasKeyShare bool + hasEarlyData bool + keyShare keyShareEntry + supportedPoints []uint8 + serverNameAck bool +} + +func (m *serverExtensions) marshal(extensions *byteBuilder) { + if m.duplicateExtension { + // Add a duplicate bogus extension at the beginning and end. + extensions.addU16(0xffff) + extensions.addU16(0) // length = 0 for empty extension + } + if m.nextProtoNeg && !m.npnAfterAlpn { + extensions.addU16(extensionNextProtoNeg) + extension := extensions.addU16LengthPrefixed() + + for _, v := range m.nextProtos { + if len(v) > 255 { + v = v[:255] + } + npn := extension.addU8LengthPrefixed() + npn.addBytes([]byte(v)) + } + } + if m.ocspStapling { + extensions.addU16(extensionStatusRequest) + extensions.addU16(0) + } + if m.ticketSupported { + extensions.addU16(extensionSessionTicket) + extensions.addU16(0) + } + if m.secureRenegotiation != nil { + extensions.addU16(extensionRenegotiationInfo) + extension := extensions.addU16LengthPrefixed() + secureRenego := extension.addU8LengthPrefixed() + secureRenego.addBytes(m.secureRenegotiation) + } + if len(m.alpnProtocol) > 0 || m.alpnProtocolEmpty { + extensions.addU16(extensionALPN) + extension := extensions.addU16LengthPrefixed() + + protocolNameList := extension.addU16LengthPrefixed() + protocolName := protocolNameList.addU8LengthPrefixed() + protocolName.addBytes([]byte(m.alpnProtocol)) + } + if m.channelIDRequested { + extensions.addU16(extensionChannelID) + extensions.addU16(0) + } + if m.duplicateExtension { + // Add a duplicate bogus extension at the beginning and end. + extensions.addU16(0xffff) + extensions.addU16(0) + } + if m.extendedMasterSecret { + extensions.addU16(extensionExtendedMasterSecret) + extensions.addU16(0) + } + if m.srtpProtectionProfile != 0 { + extensions.addU16(extensionUseSRTP) + extension := extensions.addU16LengthPrefixed() + + srtpProtectionProfiles := extension.addU16LengthPrefixed() + srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile >> 8)) + srtpProtectionProfiles.addU8(byte(m.srtpProtectionProfile)) + srtpMki := extension.addU8LengthPrefixed() + srtpMki.addBytes([]byte(m.srtpMasterKeyIdentifier)) + } + if m.sctList != nil { + extensions.addU16(extensionSignedCertificateTimestamp) + extension := extensions.addU16LengthPrefixed() + extension.addBytes(m.sctList) + } + if l := len(m.customExtension); l > 0 { + extensions.addU16(extensionCustom) + customExt := extensions.addU16LengthPrefixed() + customExt.addBytes([]byte(m.customExtension)) + } + if m.nextProtoNeg && m.npnAfterAlpn { + extensions.addU16(extensionNextProtoNeg) + extension := extensions.addU16LengthPrefixed() + + for _, v := range m.nextProtos { + if len(v) > 255 { + v = v[0:255] + } + npn := extension.addU8LengthPrefixed() + npn.addBytes([]byte(v)) + } + } + if m.hasKeyShare { + extensions.addU16(extensionKeyShare) + keyShare := extensions.addU16LengthPrefixed() + keyShare.addU16(uint16(m.keyShare.group)) + keyExchange := keyShare.addU16LengthPrefixed() + keyExchange.addBytes(m.keyShare.keyExchange) + } + if len(m.supportedPoints) > 0 { + // http://tools.ietf.org/html/rfc4492#section-5.1.2 + extensions.addU16(extensionSupportedPoints) + supportedPointsList := extensions.addU16LengthPrefixed() + supportedPoints := supportedPointsList.addU8LengthPrefixed() + supportedPoints.addBytes(m.supportedPoints) + } + if m.hasEarlyData { + extensions.addU16(extensionEarlyData) + extensions.addBytes([]byte{0, 0}) + } + if m.serverNameAck { + extensions.addU16(extensionServerName) + extensions.addU16(0) // zero length + } +} + +func (m *serverExtensions) unmarshal(data []byte, version uint16) bool { + // Reset all fields. + *m = serverExtensions{} + + for len(data) != 0 { + if len(data) < 4 { + return false + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) < length { + return false + } + + switch extension { + case extensionNextProtoNeg: + m.nextProtoNeg = true + d := data[:length] + for len(d) > 0 { + l := int(d[0]) + d = d[1:] + if l == 0 || l > len(d) { + return false + } + m.nextProtos = append(m.nextProtos, string(d[:l])) + d = d[l:] + } + case extensionStatusRequest: + if length > 0 { + return false + } + m.ocspStapling = true + case extensionSessionTicket: + if length > 0 { + return false + } + m.ticketSupported = true + case extensionRenegotiationInfo: + if length < 1 || length != int(data[0])+1 { + return false + } + m.secureRenegotiation = data[1:length] + case extensionALPN: + d := data[:length] + if len(d) < 3 { + return false + } + l := int(d[0])<<8 | int(d[1]) + if l != len(d)-2 { + return false + } + d = d[2:] + l = int(d[0]) + if l != len(d)-1 { + return false + } + d = d[1:] + m.alpnProtocol = string(d) + m.alpnProtocolEmpty = len(d) == 0 + case extensionChannelID: + if length > 0 { + return false + } + m.channelIDRequested = true + case extensionExtendedMasterSecret: + if length != 0 { + return false + } + m.extendedMasterSecret = true + case extensionUseSRTP: + if length < 2+2+1 { + return false + } + if data[0] != 0 || data[1] != 2 { + return false + } + m.srtpProtectionProfile = uint16(data[2])<<8 | uint16(data[3]) + d := data[4:length] + l := int(d[0]) + if l != len(d)-1 { + return false + } + m.srtpMasterKeyIdentifier = string(d[1:]) + case extensionSignedCertificateTimestamp: + m.sctList = data[:length] + case extensionCustom: + m.customExtension = string(data[:length]) + case extensionServerName: + if length != 0 { + return false + } + m.serverNameAck = true + case extensionSupportedPoints: + // supported_points is illegal in TLS 1.3. + if version >= VersionTLS13 { + return false + } + // http://tools.ietf.org/html/rfc4492#section-5.5.2 + if length < 1 { + return false + } + l := int(data[0]) + if length != l+1 { + return false + } + m.supportedPoints = data[1 : 1+l] + case extensionSupportedCurves: + // The server can only send supported_curves in TLS 1.3. + if version < VersionTLS13 { + return false + } + case extensionEarlyData: + if version < VersionTLS13 || length != 0 { + return false + } + m.hasEarlyData = true + default: + // Unknown extensions are illegal from the server. + return false + } + data = data[length:] + } + + return true +} + +type helloRetryRequestMsg struct { + raw []byte + vers uint16 + hasSelectedGroup bool + selectedGroup CurveID + cookie []byte + customExtension string + duplicateExtensions bool +} + +func (m *helloRetryRequestMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + retryRequestMsg := newByteBuilder() + retryRequestMsg.addU8(typeHelloRetryRequest) + retryRequest := retryRequestMsg.addU24LengthPrefixed() + retryRequest.addU16(m.vers) + extensions := retryRequest.addU16LengthPrefixed() + + count := 1 + if m.duplicateExtensions { + count = 2 + } + + for i := 0; i < count; i++ { + if m.hasSelectedGroup { + extensions.addU16(extensionKeyShare) + extensions.addU16(2) // length + extensions.addU16(uint16(m.selectedGroup)) + } + if len(m.cookie) > 0 { + extensions.addU16(extensionCookie) + body := extensions.addU16LengthPrefixed() + body.addU16LengthPrefixed().addBytes(m.cookie) + } + if len(m.customExtension) > 0 { + extensions.addU16(extensionCustom) + extensions.addU16LengthPrefixed().addBytes([]byte(m.customExtension)) + } + } + + m.raw = retryRequestMsg.finish() + return m.raw +} + +func (m *helloRetryRequestMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 8 { + return false + } + m.vers = uint16(data[4])<<8 | uint16(data[5]) + extLen := int(data[6])<<8 | int(data[7]) + data = data[8:] + if len(data) != extLen || len(data) == 0 { + return false + } + for len(data) > 0 { + if len(data) < 4 { + return false + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) < length { + return false + } + + switch extension { + case extensionKeyShare: + if length != 2 { + return false + } + m.hasSelectedGroup = true + m.selectedGroup = CurveID(data[0])<<8 | CurveID(data[1]) + case extensionCookie: + if length < 2 { + return false + } + cookieLen := int(data[0])<<8 | int(data[1]) + if 2+cookieLen != length { + return false + } + m.cookie = data[2 : 2+cookieLen] + default: + // Unknown extensions are illegal from the server. + return false + } + data = data[length:] + } + return true +} + +type certificateEntry struct { + data []byte + ocspResponse []byte + sctList []byte + duplicateExtensions bool + extraExtension []byte +} + +type certificateMsg struct { + raw []byte + hasRequestContext bool + requestContext []byte + certificates []certificateEntry +} + +func (m *certificateMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + certMsg := newByteBuilder() + certMsg.addU8(typeCertificate) + certificate := certMsg.addU24LengthPrefixed() + if m.hasRequestContext { + context := certificate.addU8LengthPrefixed() + context.addBytes(m.requestContext) + } + certificateList := certificate.addU24LengthPrefixed() + for _, cert := range m.certificates { + certEntry := certificateList.addU24LengthPrefixed() + certEntry.addBytes(cert.data) + if m.hasRequestContext { + extensions := certificateList.addU16LengthPrefixed() + count := 1 + if cert.duplicateExtensions { + count = 2 + } + + for i := 0; i < count; i++ { + if cert.ocspResponse != nil { + extensions.addU16(extensionStatusRequest) + body := extensions.addU16LengthPrefixed() + body.addU8(statusTypeOCSP) + response := body.addU24LengthPrefixed() + response.addBytes(cert.ocspResponse) + } + + if cert.sctList != nil { + extensions.addU16(extensionSignedCertificateTimestamp) + extension := extensions.addU16LengthPrefixed() + extension.addBytes(cert.sctList) + } + } + if cert.extraExtension != nil { + extensions.addBytes(cert.extraExtension) + } + } + } + + m.raw = certMsg.finish() + return m.raw +} + +func (m *certificateMsg) unmarshal(data []byte) bool { + if len(data) < 4 { + return false + } + + m.raw = data + data = data[4:] + + if m.hasRequestContext { + if len(data) == 0 { + return false + } + contextLen := int(data[0]) + if len(data) < 1+contextLen { + return false + } + m.requestContext = make([]byte, contextLen) + copy(m.requestContext, data[1:]) + data = data[1+contextLen:] + } + + if len(data) < 3 { + return false + } + certsLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2]) + data = data[3:] + if len(data) != certsLen { + return false + } + + m.certificates = nil + for len(data) != 0 { + if len(data) < 3 { + return false + } + certLen := int(data[0])<<16 | int(data[1])<<8 | int(data[2]) + if len(data) < 3+certLen { + return false + } + cert := certificateEntry{ + data: data[3 : 3+certLen], + } + data = data[3+certLen:] + if m.hasRequestContext { + if len(data) < 2 { + return false + } + extensionsLen := int(data[0])<<8 | int(data[1]) + if len(data) < 2+extensionsLen { + return false + } + extensions := data[2 : 2+extensionsLen] + data = data[2+extensionsLen:] + for len(extensions) != 0 { + if len(extensions) < 4 { + return false + } + extension := uint16(extensions[0])<<8 | uint16(extensions[1]) + length := int(extensions[2])<<8 | int(extensions[3]) + if len(extensions) < 4+length { + return false + } + contents := extensions[4 : 4+length] + extensions = extensions[4+length:] + + switch extension { + case extensionStatusRequest: + if length < 4 { + return false + } + if contents[0] != statusTypeOCSP { + return false + } + respLen := int(contents[1])<<16 | int(contents[2])<<8 | int(contents[3]) + if respLen+4 != len(contents) || respLen == 0 { + return false + } + cert.ocspResponse = contents[4:] + case extensionSignedCertificateTimestamp: + cert.sctList = contents + default: + return false + } + } + } + m.certificates = append(m.certificates, cert) + } + + return true +} + +type serverKeyExchangeMsg struct { + raw []byte + key []byte +} + +func (m *serverKeyExchangeMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + length := len(m.key) + x := make([]byte, length+4) + x[0] = typeServerKeyExchange + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + copy(x[4:], m.key) + + m.raw = x + return x +} + +func (m *serverKeyExchangeMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 4 { + return false + } + m.key = data[4:] + return true +} + +type certificateStatusMsg struct { + raw []byte + statusType uint8 + response []byte +} + +func (m *certificateStatusMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + var x []byte + if m.statusType == statusTypeOCSP { + x = make([]byte, 4+4+len(m.response)) + x[0] = typeCertificateStatus + l := len(m.response) + 4 + x[1] = byte(l >> 16) + x[2] = byte(l >> 8) + x[3] = byte(l) + x[4] = statusTypeOCSP + + l -= 4 + x[5] = byte(l >> 16) + x[6] = byte(l >> 8) + x[7] = byte(l) + copy(x[8:], m.response) + } else { + x = []byte{typeCertificateStatus, 0, 0, 1, m.statusType} + } + + m.raw = x + return x +} + +func (m *certificateStatusMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 5 { + return false + } + m.statusType = data[4] + + m.response = nil + if m.statusType == statusTypeOCSP { + if len(data) < 8 { + return false + } + respLen := uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7]) + if uint32(len(data)) != 4+4+respLen { + return false + } + m.response = data[8:] + } + return true +} + +type serverHelloDoneMsg struct{} + +func (m *serverHelloDoneMsg) marshal() []byte { + x := make([]byte, 4) + x[0] = typeServerHelloDone + return x +} + +func (m *serverHelloDoneMsg) unmarshal(data []byte) bool { + return len(data) == 4 +} + +type clientKeyExchangeMsg struct { + raw []byte + ciphertext []byte +} + +func (m *clientKeyExchangeMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + length := len(m.ciphertext) + x := make([]byte, length+4) + x[0] = typeClientKeyExchange + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + copy(x[4:], m.ciphertext) + + m.raw = x + return x +} + +func (m *clientKeyExchangeMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 4 { + return false + } + l := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + if l != len(data)-4 { + return false + } + m.ciphertext = data[4:] + return true +} + +type finishedMsg struct { + raw []byte + verifyData []byte +} + +func (m *finishedMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + x = make([]byte, 4+len(m.verifyData)) + x[0] = typeFinished + x[3] = byte(len(m.verifyData)) + copy(x[4:], m.verifyData) + m.raw = x + return +} + +func (m *finishedMsg) unmarshal(data []byte) bool { + m.raw = data + if len(data) < 4 { + return false + } + m.verifyData = data[4:] + return true +} + +type nextProtoMsg struct { + raw []byte + proto string +} + +func (m *nextProtoMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + l := len(m.proto) + if l > 255 { + l = 255 + } + + padding := 32 - (l+2)%32 + length := l + padding + 2 + x := make([]byte, length+4) + x[0] = typeNextProtocol + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + + y := x[4:] + y[0] = byte(l) + copy(y[1:], []byte(m.proto[0:l])) + y = y[1+l:] + y[0] = byte(padding) + + m.raw = x + + return x +} + +func (m *nextProtoMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 5 { + return false + } + data = data[4:] + protoLen := int(data[0]) + data = data[1:] + if len(data) < protoLen { + return false + } + m.proto = string(data[0:protoLen]) + data = data[protoLen:] + + if len(data) < 1 { + return false + } + paddingLen := int(data[0]) + data = data[1:] + if len(data) != paddingLen { + return false + } + + return true +} + +type certificateRequestMsg struct { + raw []byte + // hasSignatureAlgorithm indicates whether this message includes a list + // of signature and hash functions. This change was introduced with TLS + // 1.2. + hasSignatureAlgorithm bool + // hasRequestContext indicates whether this message includes a context + // field instead of certificateTypes. This change was introduced with + // TLS 1.3. + hasRequestContext bool + + certificateTypes []byte + requestContext []byte + signatureAlgorithms []signatureAlgorithm + certificateAuthorities [][]byte +} + +func (m *certificateRequestMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + // See http://tools.ietf.org/html/rfc4346#section-7.4.4 + builder := newByteBuilder() + builder.addU8(typeCertificateRequest) + body := builder.addU24LengthPrefixed() + + if m.hasRequestContext { + requestContext := body.addU8LengthPrefixed() + requestContext.addBytes(m.requestContext) + } else { + certificateTypes := body.addU8LengthPrefixed() + certificateTypes.addBytes(m.certificateTypes) + } + + if m.hasSignatureAlgorithm { + signatureAlgorithms := body.addU16LengthPrefixed() + for _, sigAlg := range m.signatureAlgorithms { + signatureAlgorithms.addU16(uint16(sigAlg)) + } + } + + certificateAuthorities := body.addU16LengthPrefixed() + for _, ca := range m.certificateAuthorities { + caEntry := certificateAuthorities.addU16LengthPrefixed() + caEntry.addBytes(ca) + } + + if m.hasRequestContext { + // Emit no certificate extensions. + body.addU16(0) + } + + m.raw = builder.finish() + return m.raw +} + +func (m *certificateRequestMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 5 { + return false + } + data = data[4:] + + if m.hasRequestContext { + contextLen := int(data[0]) + if len(data) < 1+contextLen { + return false + } + m.requestContext = make([]byte, contextLen) + copy(m.requestContext, data[1:]) + data = data[1+contextLen:] + } else { + numCertTypes := int(data[0]) + if len(data) < 1+numCertTypes { + return false + } + m.certificateTypes = make([]byte, numCertTypes) + copy(m.certificateTypes, data[1:]) + data = data[1+numCertTypes:] + } + + if m.hasSignatureAlgorithm { + if len(data) < 2 { + return false + } + sigAlgsLen := uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + if sigAlgsLen&1 != 0 { + return false + } + if len(data) < int(sigAlgsLen) { + return false + } + numSigAlgs := sigAlgsLen / 2 + m.signatureAlgorithms = make([]signatureAlgorithm, numSigAlgs) + for i := range m.signatureAlgorithms { + m.signatureAlgorithms[i] = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1]) + data = data[2:] + } + } + + if len(data) < 2 { + return false + } + casLength := uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + if len(data) < int(casLength) { + return false + } + cas := make([]byte, casLength) + copy(cas, data) + data = data[casLength:] + + m.certificateAuthorities = nil + for len(cas) > 0 { + if len(cas) < 2 { + return false + } + caLen := uint16(cas[0])<<8 | uint16(cas[1]) + cas = cas[2:] + + if len(cas) < int(caLen) { + return false + } + + m.certificateAuthorities = append(m.certificateAuthorities, cas[:caLen]) + cas = cas[caLen:] + } + + if m.hasRequestContext { + // Ignore certificate extensions. + if len(data) < 2 { + return false + } + extsLength := int(data[0])<<8 | int(data[1]) + if len(data) < 2+extsLength { + return false + } + data = data[2+extsLength:] + } + + if len(data) > 0 { + return false + } + + return true +} + +type certificateVerifyMsg struct { + raw []byte + hasSignatureAlgorithm bool + signatureAlgorithm signatureAlgorithm + signature []byte +} + +func (m *certificateVerifyMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See http://tools.ietf.org/html/rfc4346#section-7.4.8 + siglength := len(m.signature) + length := 2 + siglength + if m.hasSignatureAlgorithm { + length += 2 + } + x = make([]byte, 4+length) + x[0] = typeCertificateVerify + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + y := x[4:] + if m.hasSignatureAlgorithm { + y[0] = byte(m.signatureAlgorithm >> 8) + y[1] = byte(m.signatureAlgorithm) + y = y[2:] + } + y[0] = uint8(siglength >> 8) + y[1] = uint8(siglength) + copy(y[2:], m.signature) + + m.raw = x + + return +} + +func (m *certificateVerifyMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 6 { + return false + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return false + } + + data = data[4:] + if m.hasSignatureAlgorithm { + m.signatureAlgorithm = signatureAlgorithm(data[0])<<8 | signatureAlgorithm(data[1]) + data = data[2:] + } + + if len(data) < 2 { + return false + } + siglength := int(data[0])<<8 + int(data[1]) + data = data[2:] + if len(data) != siglength { + return false + } + + m.signature = data + + return true +} + +type newSessionTicketMsg struct { + raw []byte + version uint16 + ticketLifetime uint32 + ticketAgeAdd uint32 + ticket []byte + maxEarlyDataSize uint32 + customExtension string + duplicateEarlyDataInfo bool + hasGREASEExtension bool +} + +func (m *newSessionTicketMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + // See http://tools.ietf.org/html/rfc5077#section-3.3 + ticketMsg := newByteBuilder() + ticketMsg.addU8(typeNewSessionTicket) + body := ticketMsg.addU24LengthPrefixed() + body.addU32(m.ticketLifetime) + if m.version >= VersionTLS13 { + body.addU32(m.ticketAgeAdd) + } + + ticket := body.addU16LengthPrefixed() + ticket.addBytes(m.ticket) + + if m.version >= VersionTLS13 { + extensions := body.addU16LengthPrefixed() + if m.maxEarlyDataSize > 0 { + extensions.addU16(extensionTicketEarlyDataInfo) + extensions.addU16LengthPrefixed().addU32(m.maxEarlyDataSize) + if m.duplicateEarlyDataInfo { + extensions.addU16(extensionTicketEarlyDataInfo) + extensions.addU16LengthPrefixed().addU32(m.maxEarlyDataSize) + } + } + if len(m.customExtension) > 0 { + extensions.addU16(extensionCustom) + extensions.addU16LengthPrefixed().addBytes([]byte(m.customExtension)) + } + } + + m.raw = ticketMsg.finish() + return m.raw +} + +func (m *newSessionTicketMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 8 { + return false + } + m.ticketLifetime = uint32(data[4])<<24 | uint32(data[5])<<16 | uint32(data[6])<<8 | uint32(data[7]) + data = data[8:] + + if m.version >= VersionTLS13 { + if len(data) < 4 { + return false + } + m.ticketAgeAdd = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + data = data[4:] + } + + if len(data) < 2 { + return false + } + ticketLen := int(data[0])<<8 + int(data[1]) + data = data[2:] + if len(data) < ticketLen { + return false + } + + if m.version >= VersionTLS13 && ticketLen == 0 { + return false + } + + m.ticket = data[:ticketLen] + data = data[ticketLen:] + + if m.version >= VersionTLS13 { + if len(data) < 2 { + return false + } + + extensionsLength := int(data[0])<<8 | int(data[1]) + data = data[2:] + if extensionsLength != len(data) { + return false + } + + for len(data) != 0 { + if len(data) < 4 { + return false + } + extension := uint16(data[0])<<8 | uint16(data[1]) + length := int(data[2])<<8 | int(data[3]) + data = data[4:] + if len(data) < length { + return false + } + + switch extension { + case extensionTicketEarlyDataInfo: + if length != 4 { + return false + } + m.maxEarlyDataSize = uint32(data[0])<<24 | uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + default: + if isGREASEValue(extension) { + m.hasGREASEExtension = true + } + } + + data = data[length:] + } + } + + if len(data) > 0 { + return false + } + + return true +} + +type v2ClientHelloMsg struct { + raw []byte + vers uint16 + cipherSuites []uint16 + sessionId []byte + challenge []byte +} + +func (m *v2ClientHelloMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + length := 1 + 2 + 2 + 2 + 2 + len(m.cipherSuites)*3 + len(m.sessionId) + len(m.challenge) + + x := make([]byte, length) + x[0] = 1 + x[1] = uint8(m.vers >> 8) + x[2] = uint8(m.vers) + x[3] = uint8((len(m.cipherSuites) * 3) >> 8) + x[4] = uint8(len(m.cipherSuites) * 3) + x[5] = uint8(len(m.sessionId) >> 8) + x[6] = uint8(len(m.sessionId)) + x[7] = uint8(len(m.challenge) >> 8) + x[8] = uint8(len(m.challenge)) + y := x[9:] + for i, spec := range m.cipherSuites { + y[i*3] = 0 + y[i*3+1] = uint8(spec >> 8) + y[i*3+2] = uint8(spec) + } + y = y[len(m.cipherSuites)*3:] + copy(y, m.sessionId) + y = y[len(m.sessionId):] + copy(y, m.challenge) + + m.raw = x + + return x +} + +type helloVerifyRequestMsg struct { + raw []byte + vers uint16 + cookie []byte +} + +func (m *helloVerifyRequestMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + length := 2 + 1 + len(m.cookie) + + x := make([]byte, 4+length) + x[0] = typeHelloVerifyRequest + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + vers := m.vers + x[4] = uint8(vers >> 8) + x[5] = uint8(vers) + x[6] = uint8(len(m.cookie)) + copy(x[7:7+len(m.cookie)], m.cookie) + + return x +} + +func (m *helloVerifyRequestMsg) unmarshal(data []byte) bool { + if len(data) < 4+2+1 { + return false + } + m.raw = data + m.vers = uint16(data[4])<<8 | uint16(data[5]) + cookieLen := int(data[6]) + if cookieLen > 32 || len(data) != 7+cookieLen { + return false + } + m.cookie = data[7 : 7+cookieLen] + + return true +} + +type channelIDMsg struct { + raw []byte + channelID []byte +} + +func (m *channelIDMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + length := 2 + 2 + len(m.channelID) + + x := make([]byte, 4+length) + x[0] = typeChannelID + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + x[4] = uint8(extensionChannelID >> 8) + x[5] = uint8(extensionChannelID & 0xff) + x[6] = uint8(len(m.channelID) >> 8) + x[7] = uint8(len(m.channelID) & 0xff) + copy(x[8:], m.channelID) + + return x +} + +func (m *channelIDMsg) unmarshal(data []byte) bool { + if len(data) != 4+2+2+128 { + return false + } + m.raw = data + if (uint16(data[4])<<8)|uint16(data[5]) != extensionChannelID { + return false + } + if int(data[6])<<8|int(data[7]) != 128 { + return false + } + m.channelID = data[4+2+2:] + + return true +} + +type helloRequestMsg struct { +} + +func (*helloRequestMsg) marshal() []byte { + return []byte{typeHelloRequest, 0, 0, 0} +} + +func (*helloRequestMsg) unmarshal(data []byte) bool { + return len(data) == 4 +} + +type keyUpdateMsg struct { + raw []byte + keyUpdateRequest byte +} + +func (m *keyUpdateMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + return []byte{typeKeyUpdate, 0, 0, 1, m.keyUpdateRequest} +} + +func (m *keyUpdateMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) != 5 { + return false + } + + length := int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + if len(data)-4 != length { + return false + } + + m.keyUpdateRequest = data[4] + return m.keyUpdateRequest == keyUpdateNotRequested || m.keyUpdateRequest == keyUpdateRequested +} + +// ssl3NoCertificateMsg is a dummy message to handle SSL 3.0 using a warning +// alert in the handshake. +type ssl3NoCertificateMsg struct{} + +func eqUint16s(x, y []uint16) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqCurveIDs(x, y []CurveID) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqStrings(x, y []string) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i] != v { + return false + } + } + return true +} + +func eqByteSlices(x, y [][]byte) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if !bytes.Equal(v, y[i]) { + return false + } + } + return true +} + +func eqSignatureAlgorithms(x, y []signatureAlgorithm) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + v2 := y[i] + if v != v2 { + return false + } + } + return true +} + +func eqKeyShareEntryLists(x, y []keyShareEntry) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if y[i].group != v.group || !bytes.Equal(y[i].keyExchange, v.keyExchange) { + return false + } + } + return true + +} + +func eqPSKIdentityLists(x, y []pskIdentity) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + if !bytes.Equal(y[i].ticket, v.ticket) || y[i].obfuscatedTicketAge != v.obfuscatedTicketAge { + return false + } + } + return true + +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_server.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_server.go new file mode 100644 index 000000000..64edd0163 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/handshake_server.go @@ -0,0 +1,1975 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/subtle" + "crypto/x509" + "errors" + "fmt" + "io" + "math/big" + "time" +) + +// serverHandshakeState contains details of a server handshake in progress. +// It's discarded once the handshake has completed. +type serverHandshakeState struct { + c *Conn + clientHello *clientHelloMsg + hello *serverHelloMsg + suite *cipherSuite + ellipticOk bool + ecdsaOk bool + sessionState *sessionState + finishedHash finishedHash + masterSecret []byte + certsFromClient [][]byte + cert *Certificate + finishedBytes []byte +} + +// serverHandshake performs a TLS handshake as a server. +func (c *Conn) serverHandshake() error { + config := c.config + + // If this is the first server handshake, we generate a random key to + // encrypt the tickets with. + config.serverInitOnce.Do(config.serverInit) + + c.sendHandshakeSeq = 0 + c.recvHandshakeSeq = 0 + + hs := serverHandshakeState{ + c: c, + } + if err := hs.readClientHello(); err != nil { + return err + } + + if c.vers >= VersionTLS13 { + if err := hs.doTLS13Handshake(); err != nil { + return err + } + } else { + isResume, err := hs.processClientHello() + if err != nil { + return err + } + + // For an overview of TLS handshaking, see https://tools.ietf.org/html/rfc5246#section-7.3 + if isResume { + // The client has included a session ticket and so we do an abbreviated handshake. + if err := hs.doResumeHandshake(); err != nil { + return err + } + if err := hs.establishKeys(); err != nil { + return err + } + if c.config.Bugs.RenewTicketOnResume { + if err := hs.sendSessionTicket(); err != nil { + return err + } + } + if err := hs.sendFinished(c.firstFinished[:]); err != nil { + return err + } + // Most retransmits are triggered by a timeout, but the final + // leg of the handshake is retransmited upon re-receiving a + // Finished. + if err := c.simulatePacketLoss(func() { + c.sendHandshakeSeq-- + c.writeRecord(recordTypeHandshake, hs.finishedBytes) + c.flushHandshake() + }); err != nil { + return err + } + if err := hs.readFinished(nil, isResume); err != nil { + return err + } + c.didResume = true + } else { + // The client didn't include a session ticket, or it wasn't + // valid so we do a full handshake. + if err := hs.doFullHandshake(); err != nil { + return err + } + if err := hs.establishKeys(); err != nil { + return err + } + if err := hs.readFinished(c.firstFinished[:], isResume); err != nil { + return err + } + if c.config.Bugs.AlertBeforeFalseStartTest != 0 { + c.sendAlert(c.config.Bugs.AlertBeforeFalseStartTest) + } + if c.config.Bugs.ExpectFalseStart { + if err := c.readRecord(recordTypeApplicationData); err != nil { + return fmt.Errorf("tls: peer did not false start: %s", err) + } + } + if err := hs.sendSessionTicket(); err != nil { + return err + } + if err := hs.sendFinished(nil); err != nil { + return err + } + } + + c.exporterSecret = hs.masterSecret + } + c.handshakeComplete = true + copy(c.clientRandom[:], hs.clientHello.random) + copy(c.serverRandom[:], hs.hello.random) + + return nil +} + +// readClientHello reads a ClientHello message from the client and determines +// the protocol version. +func (hs *serverHandshakeState) readClientHello() error { + config := hs.c.config + c := hs.c + + if err := c.simulatePacketLoss(nil); err != nil { + return err + } + msg, err := c.readHandshake() + if err != nil { + return err + } + var ok bool + hs.clientHello, ok = msg.(*clientHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(hs.clientHello, msg) + } + if size := config.Bugs.RequireClientHelloSize; size != 0 && len(hs.clientHello.raw) != size { + return fmt.Errorf("tls: ClientHello record size is %d, but expected %d", len(hs.clientHello.raw), size) + } + + if c.isDTLS && !config.Bugs.SkipHelloVerifyRequest { + // Per RFC 6347, the version field in HelloVerifyRequest SHOULD + // be always DTLS 1.0 + helloVerifyRequest := &helloVerifyRequestMsg{ + vers: versionToWire(VersionTLS10, c.isDTLS), + cookie: make([]byte, 32), + } + if _, err := io.ReadFull(c.config.rand(), helloVerifyRequest.cookie); err != nil { + c.sendAlert(alertInternalError) + return errors.New("dtls: short read from Rand: " + err.Error()) + } + c.writeRecord(recordTypeHandshake, helloVerifyRequest.marshal()) + c.flushHandshake() + + if err := c.simulatePacketLoss(nil); err != nil { + return err + } + msg, err := c.readHandshake() + if err != nil { + return err + } + newClientHello, ok := msg.(*clientHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(hs.clientHello, msg) + } + if !bytes.Equal(newClientHello.cookie, helloVerifyRequest.cookie) { + return errors.New("dtls: invalid cookie") + } + + // Apart from the cookie, the two ClientHellos must + // match. Note that clientHello.equal compares the + // serialization, so we make a copy. + oldClientHelloCopy := *hs.clientHello + oldClientHelloCopy.raw = nil + oldClientHelloCopy.cookie = nil + newClientHelloCopy := *newClientHello + newClientHelloCopy.raw = nil + newClientHelloCopy.cookie = nil + if !oldClientHelloCopy.equal(&newClientHelloCopy) { + return errors.New("dtls: retransmitted ClientHello does not match") + } + hs.clientHello = newClientHello + } + + if config.Bugs.RequireSameRenegoClientVersion && c.clientVersion != 0 { + if c.clientVersion != hs.clientHello.vers { + return fmt.Errorf("tls: client offered different version on renego") + } + } + + c.clientVersion = hs.clientHello.vers + + // Convert the ClientHello wire version to a protocol version. + var clientVersion uint16 + if c.isDTLS { + if hs.clientHello.vers <= 0xfefd { + clientVersion = VersionTLS12 + } else if hs.clientHello.vers <= 0xfeff { + clientVersion = VersionTLS10 + } + } else { + if hs.clientHello.vers >= VersionTLS12 { + clientVersion = VersionTLS12 + } else if hs.clientHello.vers >= VersionTLS11 { + clientVersion = VersionTLS11 + } else if hs.clientHello.vers >= VersionTLS10 { + clientVersion = VersionTLS10 + } else if hs.clientHello.vers >= VersionSSL30 { + clientVersion = VersionSSL30 + } + } + + if config.Bugs.NegotiateVersion != 0 { + c.vers = config.Bugs.NegotiateVersion + } else if c.haveVers && config.Bugs.NegotiateVersionOnRenego != 0 { + c.vers = config.Bugs.NegotiateVersionOnRenego + } else if len(hs.clientHello.supportedVersions) > 0 { + // Use the versions extension if supplied. + var foundVersion, foundGREASE bool + for _, extVersion := range hs.clientHello.supportedVersions { + if isGREASEValue(extVersion) { + foundGREASE = true + } + extVersion, ok = wireToVersion(extVersion, c.isDTLS) + if !ok { + continue + } + if config.isSupportedVersion(extVersion, c.isDTLS) && !foundVersion { + c.vers = extVersion + foundVersion = true + break + } + } + if !foundVersion { + c.sendAlert(alertProtocolVersion) + return errors.New("tls: client did not offer any supported protocol versions") + } + if config.Bugs.ExpectGREASE && !foundGREASE { + return errors.New("tls: no GREASE version value found") + } + } else { + // Otherwise, use the legacy ClientHello version. + version := clientVersion + if maxVersion := config.maxVersion(c.isDTLS); version > maxVersion { + version = maxVersion + } + if version == 0 || !config.isSupportedVersion(version, c.isDTLS) { + return fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers) + } + c.vers = version + } + c.haveVers = true + + // Reject < 1.2 ClientHellos with signature_algorithms. + if clientVersion < VersionTLS12 && len(hs.clientHello.signatureAlgorithms) > 0 { + return fmt.Errorf("tls: client included signature_algorithms before TLS 1.2") + } + + // Check the client cipher list is consistent with the version. + if clientVersion < VersionTLS12 { + for _, id := range hs.clientHello.cipherSuites { + if isTLS12Cipher(id) { + return fmt.Errorf("tls: client offered TLS 1.2 cipher before TLS 1.2") + } + } + } + + if config.Bugs.ExpectNoTLS12Session { + if len(hs.clientHello.sessionId) > 0 { + return fmt.Errorf("tls: client offered an unexpected session ID") + } + if len(hs.clientHello.sessionTicket) > 0 { + return fmt.Errorf("tls: client offered an unexpected session ticket") + } + } + + if config.Bugs.ExpectNoTLS13PSK && len(hs.clientHello.pskIdentities) > 0 { + return fmt.Errorf("tls: client offered unexpected PSK identities") + } + + var scsvFound, greaseFound bool + for _, cipherSuite := range hs.clientHello.cipherSuites { + if cipherSuite == fallbackSCSV { + scsvFound = true + } + if isGREASEValue(cipherSuite) { + greaseFound = true + } + } + + if !scsvFound && config.Bugs.FailIfNotFallbackSCSV { + return errors.New("tls: no fallback SCSV found when expected") + } else if scsvFound && !config.Bugs.FailIfNotFallbackSCSV { + return errors.New("tls: fallback SCSV found when not expected") + } + + if !greaseFound && config.Bugs.ExpectGREASE { + return errors.New("tls: no GREASE cipher suite value found") + } + + greaseFound = false + for _, curve := range hs.clientHello.supportedCurves { + if isGREASEValue(uint16(curve)) { + greaseFound = true + break + } + } + + if !greaseFound && config.Bugs.ExpectGREASE { + return errors.New("tls: no GREASE curve value found") + } + + if len(hs.clientHello.keyShares) > 0 { + greaseFound = false + for _, keyShare := range hs.clientHello.keyShares { + if isGREASEValue(uint16(keyShare.group)) { + greaseFound = true + break + } + } + + if !greaseFound && config.Bugs.ExpectGREASE { + return errors.New("tls: no GREASE curve value found") + } + } + + applyBugsToClientHello(hs.clientHello, config) + + return nil +} + +func applyBugsToClientHello(clientHello *clientHelloMsg, config *Config) { + if config.Bugs.IgnorePeerSignatureAlgorithmPreferences { + clientHello.signatureAlgorithms = config.signSignatureAlgorithms() + } + if config.Bugs.IgnorePeerCurvePreferences { + clientHello.supportedCurves = config.curvePreferences() + } + if config.Bugs.IgnorePeerCipherPreferences { + clientHello.cipherSuites = config.cipherSuites() + } +} + +func (hs *serverHandshakeState) doTLS13Handshake() error { + c := hs.c + config := c.config + + hs.hello = &serverHelloMsg{ + isDTLS: c.isDTLS, + vers: versionToWire(c.vers, c.isDTLS), + versOverride: config.Bugs.SendServerHelloVersion, + customExtension: config.Bugs.CustomUnencryptedExtension, + unencryptedALPN: config.Bugs.SendUnencryptedALPN, + } + + hs.hello.random = make([]byte, 32) + if _, err := io.ReadFull(config.rand(), hs.hello.random); err != nil { + c.sendAlert(alertInternalError) + return err + } + + // TLS 1.3 forbids clients from advertising any non-null compression. + if len(hs.clientHello.compressionMethods) != 1 || hs.clientHello.compressionMethods[0] != compressionNone { + return errors.New("tls: client sent compression method other than null for TLS 1.3") + } + + // Prepare an EncryptedExtensions message, but do not send it yet. + encryptedExtensions := new(encryptedExtensionsMsg) + encryptedExtensions.empty = config.Bugs.EmptyEncryptedExtensions + if err := hs.processClientExtensions(&encryptedExtensions.extensions); err != nil { + return err + } + + // Select the cipher suite. + var preferenceList, supportedList []uint16 + if config.PreferServerCipherSuites { + preferenceList = config.cipherSuites() + supportedList = hs.clientHello.cipherSuites + } else { + preferenceList = hs.clientHello.cipherSuites + supportedList = config.cipherSuites() + } + + for _, id := range preferenceList { + if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, true, true); hs.suite != nil { + break + } + } + + if hs.suite == nil { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: no cipher suite supported by both client and server") + } + + hs.hello.cipherSuite = hs.suite.id + if c.config.Bugs.SendCipherSuite != 0 { + hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite + } + + hs.finishedHash = newFinishedHash(c.vers, hs.suite) + hs.finishedHash.discardHandshakeBuffer() + hs.writeClientHash(hs.clientHello.marshal()) + + supportedCurve := false + var selectedCurve CurveID + preferredCurves := config.curvePreferences() +Curves: + for _, curve := range hs.clientHello.supportedCurves { + for _, supported := range preferredCurves { + if supported == curve { + supportedCurve = true + selectedCurve = curve + break Curves + } + } + } + + if !supportedCurve { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: no curve supported by both client and server") + } + + pskIdentities := hs.clientHello.pskIdentities + pskKEModes := hs.clientHello.pskKEModes + + if len(pskIdentities) == 0 && len(hs.clientHello.sessionTicket) > 0 && c.config.Bugs.AcceptAnySession { + psk := pskIdentity{ + ticket: hs.clientHello.sessionTicket, + } + pskIdentities = []pskIdentity{psk} + pskKEModes = []byte{pskDHEKEMode} + } + + var pskIndex int + foundKEMode := bytes.IndexByte(pskKEModes, pskDHEKEMode) >= 0 + if foundKEMode { + for i, pskIdentity := range pskIdentities { + // TODO(svaldez): Check the obfuscatedTicketAge before accepting 0-RTT. + sessionState, ok := c.decryptTicket(pskIdentity.ticket) + if !ok { + continue + } + + if !config.Bugs.AcceptAnySession { + if sessionState.vers != c.vers { + continue + } + if sessionState.ticketExpiration.Before(c.config.time()) { + continue + } + sessionCipher := cipherSuiteFromID(sessionState.cipherSuite) + if sessionCipher == nil || sessionCipher.hash() != hs.suite.hash() { + continue + } + } + + clientTicketAge := time.Duration(uint32(pskIdentity.obfuscatedTicketAge-sessionState.ticketAgeAdd)) * time.Millisecond + if config.Bugs.ExpectTicketAge != 0 && clientTicketAge != config.Bugs.ExpectTicketAge { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: invalid ticket age") + } + + hs.sessionState = sessionState + hs.hello.hasPSKIdentity = true + hs.hello.pskIdentity = uint16(i) + pskIndex = i + if config.Bugs.SelectPSKIdentityOnResume != 0 { + hs.hello.pskIdentity = config.Bugs.SelectPSKIdentityOnResume + } + c.didResume = true + break + } + } + + if config.Bugs.AlwaysSelectPSKIdentity { + hs.hello.hasPSKIdentity = true + hs.hello.pskIdentity = 0 + } + + // Verify the PSK binder. Note there may not be a PSK binder if + // AcceptAnyBinder is set. See https://crbug.com/boringssl/115. + if hs.sessionState != nil && !config.Bugs.AcceptAnySession { + binderToVerify := hs.clientHello.pskBinders[pskIndex] + if err := verifyPSKBinder(hs.clientHello, hs.sessionState, binderToVerify, []byte{}); err != nil { + return err + } + } + + // Resolve PSK and compute the early secret. + if hs.sessionState != nil { + hs.finishedHash.addEntropy(hs.sessionState.masterSecret) + } else { + hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret()) + } + + hs.hello.hasKeyShare = true + if hs.sessionState != nil && config.Bugs.NegotiatePSKResumption { + hs.hello.hasKeyShare = false + } + if config.Bugs.MissingKeyShare { + hs.hello.hasKeyShare = false + } + + firstHelloRetryRequest := true + +ResendHelloRetryRequest: + var sendHelloRetryRequest bool + helloRetryRequest := &helloRetryRequestMsg{ + vers: versionToWire(c.vers, c.isDTLS), + duplicateExtensions: config.Bugs.DuplicateHelloRetryRequestExtensions, + } + + if config.Bugs.AlwaysSendHelloRetryRequest { + sendHelloRetryRequest = true + } + + if config.Bugs.SendHelloRetryRequestCookie != nil { + sendHelloRetryRequest = true + helloRetryRequest.cookie = config.Bugs.SendHelloRetryRequestCookie + } + + if len(config.Bugs.CustomHelloRetryRequestExtension) > 0 { + sendHelloRetryRequest = true + helloRetryRequest.customExtension = config.Bugs.CustomHelloRetryRequestExtension + } + + var selectedKeyShare *keyShareEntry + if hs.hello.hasKeyShare { + // Look for the key share corresponding to our selected curve. + for i := range hs.clientHello.keyShares { + if hs.clientHello.keyShares[i].group == selectedCurve { + selectedKeyShare = &hs.clientHello.keyShares[i] + break + } + } + + if config.Bugs.ExpectMissingKeyShare && selectedKeyShare != nil { + return errors.New("tls: expected missing key share") + } + + if selectedKeyShare == nil { + helloRetryRequest.hasSelectedGroup = true + helloRetryRequest.selectedGroup = selectedCurve + sendHelloRetryRequest = true + } + } + + if config.Bugs.SendHelloRetryRequestCurve != 0 { + helloRetryRequest.hasSelectedGroup = true + helloRetryRequest.selectedGroup = config.Bugs.SendHelloRetryRequestCurve + sendHelloRetryRequest = true + } + + if config.Bugs.SkipHelloRetryRequest { + sendHelloRetryRequest = false + } + + if sendHelloRetryRequest { + oldClientHelloBytes := hs.clientHello.marshal() + hs.writeServerHash(helloRetryRequest.marshal()) + c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()) + c.flushHandshake() + + // Read new ClientHello. + newMsg, err := c.readHandshake() + if err != nil { + return err + } + newClientHello, ok := newMsg.(*clientHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(newClientHello, newMsg) + } + hs.writeClientHash(newClientHello.marshal()) + + applyBugsToClientHello(newClientHello, config) + + // Check that the new ClientHello matches the old ClientHello, + // except for relevant modifications. + // + // TODO(davidben): Make this check more precise. + oldClientHelloCopy := *hs.clientHello + oldClientHelloCopy.raw = nil + oldClientHelloCopy.hasEarlyData = false + newClientHelloCopy := *newClientHello + newClientHelloCopy.raw = nil + + if helloRetryRequest.hasSelectedGroup { + newKeyShares := newClientHelloCopy.keyShares + if len(newKeyShares) != 1 || newKeyShares[0].group != helloRetryRequest.selectedGroup { + return errors.New("tls: KeyShare from HelloRetryRequest not in new ClientHello") + } + selectedKeyShare = &newKeyShares[0] + newClientHelloCopy.keyShares = oldClientHelloCopy.keyShares + } + + if len(helloRetryRequest.cookie) > 0 { + if !bytes.Equal(newClientHelloCopy.tls13Cookie, helloRetryRequest.cookie) { + return errors.New("tls: cookie from HelloRetryRequest not present in new ClientHello") + } + newClientHelloCopy.tls13Cookie = nil + } + + // PSK binders and obfuscated ticket age are both updated in the + // second ClientHello. + if len(oldClientHelloCopy.pskIdentities) != len(newClientHelloCopy.pskIdentities) { + return errors.New("tls: PSK identity count from old and new ClientHello do not match") + } + for i, identity := range oldClientHelloCopy.pskIdentities { + newClientHelloCopy.pskIdentities[i].obfuscatedTicketAge = identity.obfuscatedTicketAge + } + newClientHelloCopy.pskBinders = oldClientHelloCopy.pskBinders + + if !oldClientHelloCopy.equal(&newClientHelloCopy) { + return errors.New("tls: new ClientHello does not match") + } + + if firstHelloRetryRequest && config.Bugs.SecondHelloRetryRequest { + firstHelloRetryRequest = false + goto ResendHelloRetryRequest + } + + // Verify the PSK binder. Note there may not be a PSK binder if + // AcceptAnyBinder is set. See https://crbug.com/115. + if hs.sessionState != nil && !config.Bugs.AcceptAnySession { + binderToVerify := newClientHello.pskBinders[pskIndex] + err := verifyPSKBinder(newClientHello, hs.sessionState, binderToVerify, append(oldClientHelloBytes, helloRetryRequest.marshal()...)) + if err != nil { + return err + } + } + } + + // Decide whether or not to accept early data. + // TODO(nharper): This does not check that ALPN or SNI matches. + if hs.clientHello.hasEarlyData { + if !sendHelloRetryRequest && hs.sessionState != nil { + encryptedExtensions.extensions.hasEarlyData = true + earlyTrafficSecret := hs.finishedHash.deriveSecret(earlyTrafficLabel) + c.in.useTrafficSecret(c.vers, hs.suite, earlyTrafficSecret, clientWrite) + + for _, expectedMsg := range config.Bugs.ExpectEarlyData { + if err := c.readRecord(recordTypeApplicationData); err != nil { + return err + } + if !bytes.Equal(c.input.data[c.input.off:], expectedMsg) { + return errors.New("ExpectEarlyData: did not get expected message") + } + c.in.freeBlock(c.input) + c.input = nil + + } + } else { + c.skipEarlyData = true + } + } + + // Resolve ECDHE and compute the handshake secret. + if hs.hello.hasKeyShare { + // Once a curve has been selected and a key share identified, + // the server needs to generate a public value and send it in + // the ServerHello. + curve, ok := curveForCurveID(selectedCurve) + if !ok { + panic("tls: server failed to look up curve ID") + } + c.curveID = selectedCurve + + var peerKey []byte + if config.Bugs.SkipHelloRetryRequest { + // If skipping HelloRetryRequest, use a random key to + // avoid crashing. + curve2, _ := curveForCurveID(selectedCurve) + var err error + peerKey, err = curve2.offer(config.rand()) + if err != nil { + return err + } + } else { + peerKey = selectedKeyShare.keyExchange + } + + publicKey, ecdheSecret, err := curve.accept(config.rand(), peerKey) + if err != nil { + c.sendAlert(alertHandshakeFailure) + return err + } + hs.finishedHash.addEntropy(ecdheSecret) + hs.hello.hasKeyShare = true + + curveID := selectedCurve + if c.config.Bugs.SendCurve != 0 { + curveID = config.Bugs.SendCurve + } + if c.config.Bugs.InvalidECDHPoint { + publicKey[0] ^= 0xff + } + + hs.hello.keyShare = keyShareEntry{ + group: curveID, + keyExchange: publicKey, + } + + if config.Bugs.EncryptedExtensionsWithKeyShare { + encryptedExtensions.extensions.hasKeyShare = true + encryptedExtensions.extensions.keyShare = keyShareEntry{ + group: curveID, + keyExchange: publicKey, + } + } + } else { + hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret()) + } + + // Send unencrypted ServerHello. + hs.writeServerHash(hs.hello.marshal()) + if config.Bugs.PartialEncryptedExtensionsWithServerHello { + helloBytes := hs.hello.marshal() + toWrite := make([]byte, 0, len(helloBytes)+1) + toWrite = append(toWrite, helloBytes...) + toWrite = append(toWrite, typeEncryptedExtensions) + c.writeRecord(recordTypeHandshake, toWrite) + } else { + c.writeRecord(recordTypeHandshake, hs.hello.marshal()) + } + c.flushHandshake() + + // Switch to handshake traffic keys. + serverHandshakeTrafficSecret := hs.finishedHash.deriveSecret(serverHandshakeTrafficLabel) + c.out.useTrafficSecret(c.vers, hs.suite, serverHandshakeTrafficSecret, serverWrite) + // Derive handshake traffic read key, but don't switch yet. + clientHandshakeTrafficSecret := hs.finishedHash.deriveSecret(clientHandshakeTrafficLabel) + + // Send EncryptedExtensions. + hs.writeServerHash(encryptedExtensions.marshal()) + if config.Bugs.PartialEncryptedExtensionsWithServerHello { + // The first byte has already been sent. + c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()[1:]) + } else { + c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()) + } + + if hs.sessionState == nil { + if config.ClientAuth >= RequestClientCert { + // Request a client certificate + certReq := &certificateRequestMsg{ + hasSignatureAlgorithm: true, + hasRequestContext: true, + requestContext: config.Bugs.SendRequestContext, + } + if !config.Bugs.NoSignatureAlgorithms { + certReq.signatureAlgorithms = config.verifySignatureAlgorithms() + } + + // An empty list of certificateAuthorities signals to + // the client that it may send any certificate in response + // to our request. When we know the CAs we trust, then + // we can send them down, so that the client can choose + // an appropriate certificate to give to us. + if config.ClientCAs != nil { + certReq.certificateAuthorities = config.ClientCAs.Subjects() + } + hs.writeServerHash(certReq.marshal()) + c.writeRecord(recordTypeHandshake, certReq.marshal()) + } + + certMsg := &certificateMsg{ + hasRequestContext: true, + } + if !config.Bugs.EmptyCertificateList { + for i, certData := range hs.cert.Certificate { + cert := certificateEntry{ + data: certData, + } + if i == 0 { + if hs.clientHello.ocspStapling { + cert.ocspResponse = hs.cert.OCSPStaple + } + if hs.clientHello.sctListSupported { + cert.sctList = hs.cert.SignedCertificateTimestampList + } + cert.duplicateExtensions = config.Bugs.SendDuplicateCertExtensions + cert.extraExtension = config.Bugs.SendExtensionOnCertificate + } else { + if config.Bugs.SendOCSPOnIntermediates != nil { + cert.ocspResponse = config.Bugs.SendOCSPOnIntermediates + } + if config.Bugs.SendSCTOnIntermediates != nil { + cert.sctList = config.Bugs.SendSCTOnIntermediates + } + } + certMsg.certificates = append(certMsg.certificates, cert) + } + } + certMsgBytes := certMsg.marshal() + hs.writeServerHash(certMsgBytes) + c.writeRecord(recordTypeHandshake, certMsgBytes) + + certVerify := &certificateVerifyMsg{ + hasSignatureAlgorithm: true, + } + + // Determine the hash to sign. + privKey := hs.cert.PrivateKey + + var err error + certVerify.signatureAlgorithm, err = selectSignatureAlgorithm(c.vers, privKey, config, hs.clientHello.signatureAlgorithms) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + input := hs.finishedHash.certificateVerifyInput(serverCertificateVerifyContextTLS13) + certVerify.signature, err = signMessage(c.vers, privKey, c.config, certVerify.signatureAlgorithm, input) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + + if config.Bugs.SendSignatureAlgorithm != 0 { + certVerify.signatureAlgorithm = config.Bugs.SendSignatureAlgorithm + } + + hs.writeServerHash(certVerify.marshal()) + c.writeRecord(recordTypeHandshake, certVerify.marshal()) + } else if hs.sessionState != nil { + // Pick up certificates from the session instead. + if len(hs.sessionState.certificates) > 0 { + if _, err := hs.processCertsFromClient(hs.sessionState.certificates); err != nil { + return err + } + } + } + + finished := new(finishedMsg) + finished.verifyData = hs.finishedHash.serverSum(serverHandshakeTrafficSecret) + if config.Bugs.BadFinished { + finished.verifyData[0]++ + } + hs.writeServerHash(finished.marshal()) + c.writeRecord(recordTypeHandshake, finished.marshal()) + if c.config.Bugs.SendExtraFinished { + c.writeRecord(recordTypeHandshake, finished.marshal()) + } + c.flushHandshake() + + // The various secrets do not incorporate the client's final leg, so + // derive them now before updating the handshake context. + hs.finishedHash.addEntropy(hs.finishedHash.zeroSecret()) + clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel) + serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel) + c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel) + + // Switch to application data keys on write. In particular, any alerts + // from the client certificate are sent over these keys. + c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite) + + // Send 0.5-RTT messages. + for _, halfRTTMsg := range config.Bugs.SendHalfRTTData { + if _, err := c.writeRecord(recordTypeApplicationData, halfRTTMsg); err != nil { + return err + } + } + + // Read end_of_early_data alert. + if encryptedExtensions.extensions.hasEarlyData { + if err := c.readRecord(recordTypeAlert); err != errEndOfEarlyDataAlert { + if err == nil { + panic("readRecord(recordTypeAlert) returned nil") + } + return err + } + } + + // Switch input stream to handshake traffic keys. + c.in.useTrafficSecret(c.vers, hs.suite, clientHandshakeTrafficSecret, clientWrite) + + // If we requested a client certificate, then the client must send a + // certificate message, even if it's empty. + if config.ClientAuth >= RequestClientCert { + msg, err := c.readHandshake() + if err != nil { + return err + } + + certMsg, ok := msg.(*certificateMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + hs.writeClientHash(certMsg.marshal()) + + if len(certMsg.certificates) == 0 { + // The client didn't actually send a certificate + switch config.ClientAuth { + case RequireAnyClientCert, RequireAndVerifyClientCert: + c.sendAlert(alertCertificateRequired) + return errors.New("tls: client didn't provide a certificate") + } + } + + var certs [][]byte + for _, cert := range certMsg.certificates { + certs = append(certs, cert.data) + // OCSP responses and SCT lists are not negotiated in + // client certificates. + if cert.ocspResponse != nil || cert.sctList != nil { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: unexpected extensions in the client certificate") + } + } + pub, err := hs.processCertsFromClient(certs) + if err != nil { + return err + } + + if len(c.peerCertificates) > 0 { + msg, err = c.readHandshake() + if err != nil { + return err + } + + certVerify, ok := msg.(*certificateVerifyMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certVerify, msg) + } + + c.peerSignatureAlgorithm = certVerify.signatureAlgorithm + input := hs.finishedHash.certificateVerifyInput(clientCertificateVerifyContextTLS13) + if err := verifyMessage(c.vers, pub, config, certVerify.signatureAlgorithm, input, certVerify.signature); err != nil { + c.sendAlert(alertBadCertificate) + return err + } + hs.writeClientHash(certVerify.marshal()) + } + } + + if encryptedExtensions.extensions.channelIDRequested { + msg, err := c.readHandshake() + if err != nil { + return err + } + channelIDMsg, ok := msg.(*channelIDMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(channelIDMsg, msg) + } + channelIDHash := crypto.SHA256.New() + channelIDHash.Write(hs.finishedHash.certificateVerifyInput(channelIDContextTLS13)) + channelID, err := verifyChannelIDMessage(channelIDMsg, channelIDHash.Sum(nil)) + if err != nil { + return err + } + c.channelID = channelID + + hs.writeClientHash(channelIDMsg.marshal()) + } + + // Read the client Finished message. + msg, err := c.readHandshake() + if err != nil { + return err + } + clientFinished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(clientFinished, msg) + } + + verify := hs.finishedHash.clientSum(clientHandshakeTrafficSecret) + if len(verify) != len(clientFinished.verifyData) || + subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: client's Finished message was incorrect") + } + hs.writeClientHash(clientFinished.marshal()) + + // Switch to application data keys on read. + c.in.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite) + + c.cipherSuite = hs.suite + c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel) + + // TODO(davidben): Allow configuring the number of tickets sent for + // testing. + if !c.config.SessionTicketsDisabled && foundKEMode { + ticketCount := 2 + for i := 0; i < ticketCount; i++ { + c.SendNewSessionTicket() + } + } + return nil +} + +// processClientHello processes the ClientHello message from the client and +// decides whether we will perform session resumption. +func (hs *serverHandshakeState) processClientHello() (isResume bool, err error) { + config := hs.c.config + c := hs.c + + hs.hello = &serverHelloMsg{ + isDTLS: c.isDTLS, + vers: versionToWire(c.vers, c.isDTLS), + versOverride: config.Bugs.SendServerHelloVersion, + compressionMethod: compressionNone, + } + + hs.hello.random = make([]byte, 32) + _, err = io.ReadFull(config.rand(), hs.hello.random) + if err != nil { + c.sendAlert(alertInternalError) + return false, err + } + // Signal downgrades in the server random, per draft-ietf-tls-tls13-16, + // section 4.1.3. + if c.vers <= VersionTLS12 && config.maxVersion(c.isDTLS) >= VersionTLS13 { + copy(hs.hello.random[len(hs.hello.random)-8:], downgradeTLS13) + } + if c.vers <= VersionTLS11 && config.maxVersion(c.isDTLS) == VersionTLS12 { + copy(hs.hello.random[len(hs.hello.random)-8:], downgradeTLS12) + } + + foundCompression := false + // We only support null compression, so check that the client offered it. + for _, compression := range hs.clientHello.compressionMethods { + if compression == compressionNone { + foundCompression = true + break + } + } + + if !foundCompression { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: client does not support uncompressed connections") + } + + if err := hs.processClientExtensions(&hs.hello.extensions); err != nil { + return false, err + } + + supportedCurve := false + preferredCurves := config.curvePreferences() +Curves: + for _, curve := range hs.clientHello.supportedCurves { + for _, supported := range preferredCurves { + if supported == curve { + supportedCurve = true + break Curves + } + } + } + + supportedPointFormat := false + for _, pointFormat := range hs.clientHello.supportedPoints { + if pointFormat == pointFormatUncompressed { + supportedPointFormat = true + break + } + } + hs.ellipticOk = supportedCurve && supportedPointFormat + + _, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey) + + // For test purposes, check that the peer never offers a session when + // renegotiating. + if c.cipherSuite != nil && len(hs.clientHello.sessionId) > 0 && c.config.Bugs.FailIfResumeOnRenego { + return false, errors.New("tls: offered resumption on renegotiation") + } + + if c.config.Bugs.FailIfSessionOffered && (len(hs.clientHello.sessionTicket) > 0 || len(hs.clientHello.sessionId) > 0) { + return false, errors.New("tls: client offered a session ticket or ID") + } + + if hs.checkForResumption() { + return true, nil + } + + var preferenceList, supportedList []uint16 + if c.config.PreferServerCipherSuites { + preferenceList = c.config.cipherSuites() + supportedList = hs.clientHello.cipherSuites + } else { + preferenceList = hs.clientHello.cipherSuites + supportedList = c.config.cipherSuites() + } + + for _, id := range preferenceList { + if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil { + break + } + } + + if hs.suite == nil { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: no cipher suite supported by both client and server") + } + + return false, nil +} + +// processClientExtensions processes all ClientHello extensions not directly +// related to cipher suite negotiation and writes responses in serverExtensions. +func (hs *serverHandshakeState) processClientExtensions(serverExtensions *serverExtensions) error { + config := hs.c.config + c := hs.c + + if c.vers < VersionTLS13 || config.Bugs.NegotiateRenegotiationInfoAtAllVersions { + if !bytes.Equal(c.clientVerify, hs.clientHello.secureRenegotiation) { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: renegotiation mismatch") + } + + if len(c.clientVerify) > 0 && !c.config.Bugs.EmptyRenegotiationInfo { + serverExtensions.secureRenegotiation = append(serverExtensions.secureRenegotiation, c.clientVerify...) + serverExtensions.secureRenegotiation = append(serverExtensions.secureRenegotiation, c.serverVerify...) + if c.config.Bugs.BadRenegotiationInfo { + serverExtensions.secureRenegotiation[0] ^= 0x80 + } + } else { + serverExtensions.secureRenegotiation = hs.clientHello.secureRenegotiation + } + + if c.noRenegotiationInfo() { + serverExtensions.secureRenegotiation = nil + } + } + + serverExtensions.duplicateExtension = c.config.Bugs.DuplicateExtension + + if len(hs.clientHello.serverName) > 0 { + c.serverName = hs.clientHello.serverName + } + if len(config.Certificates) == 0 { + c.sendAlert(alertInternalError) + return errors.New("tls: no certificates configured") + } + hs.cert = &config.Certificates[0] + if len(hs.clientHello.serverName) > 0 { + hs.cert = config.getCertificateForName(hs.clientHello.serverName) + } + if expected := c.config.Bugs.ExpectServerName; expected != "" && expected != hs.clientHello.serverName { + return errors.New("tls: unexpected server name") + } + + if cert := config.Bugs.RenegotiationCertificate; c.cipherSuite != nil && cert != nil { + hs.cert = cert + } + + if len(hs.clientHello.alpnProtocols) > 0 { + if proto := c.config.Bugs.ALPNProtocol; proto != nil { + serverExtensions.alpnProtocol = *proto + serverExtensions.alpnProtocolEmpty = len(*proto) == 0 + c.clientProtocol = *proto + c.usedALPN = true + } else if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback { + serverExtensions.alpnProtocol = selectedProto + c.clientProtocol = selectedProto + c.usedALPN = true + } + } + + if len(c.config.Bugs.SendALPN) > 0 { + serverExtensions.alpnProtocol = c.config.Bugs.SendALPN + } + + if c.vers < VersionTLS13 || config.Bugs.NegotiateNPNAtAllVersions { + if len(hs.clientHello.alpnProtocols) == 0 || c.config.Bugs.NegotiateALPNAndNPN { + // Although sending an empty NPN extension is reasonable, Firefox has + // had a bug around this. Best to send nothing at all if + // config.NextProtos is empty. See + // https://code.google.com/p/go/issues/detail?id=5445. + if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 { + serverExtensions.nextProtoNeg = true + serverExtensions.nextProtos = config.NextProtos + serverExtensions.npnAfterAlpn = config.Bugs.SwapNPNAndALPN + } + } + } + + if c.vers < VersionTLS13 || config.Bugs.NegotiateEMSAtAllVersions { + disableEMS := config.Bugs.NoExtendedMasterSecret + if c.cipherSuite != nil { + disableEMS = config.Bugs.NoExtendedMasterSecretOnRenegotiation + } + serverExtensions.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !disableEMS + } + + if hs.clientHello.channelIDSupported && config.RequestChannelID { + serverExtensions.channelIDRequested = true + } + + if hs.clientHello.srtpProtectionProfiles != nil { + SRTPLoop: + for _, p1 := range c.config.SRTPProtectionProfiles { + for _, p2 := range hs.clientHello.srtpProtectionProfiles { + if p1 == p2 { + serverExtensions.srtpProtectionProfile = p1 + c.srtpProtectionProfile = p1 + break SRTPLoop + } + } + } + } + + if c.config.Bugs.SendSRTPProtectionProfile != 0 { + serverExtensions.srtpProtectionProfile = c.config.Bugs.SendSRTPProtectionProfile + } + + if expected := c.config.Bugs.ExpectedCustomExtension; expected != nil { + if hs.clientHello.customExtension != *expected { + return fmt.Errorf("tls: bad custom extension contents %q", hs.clientHello.customExtension) + } + } + serverExtensions.customExtension = config.Bugs.CustomExtension + + if c.config.Bugs.AdvertiseTicketExtension { + serverExtensions.ticketSupported = true + } + + if c.config.Bugs.SendSupportedPointFormats != nil { + serverExtensions.supportedPoints = c.config.Bugs.SendSupportedPointFormats + } + + if !hs.clientHello.hasGREASEExtension && config.Bugs.ExpectGREASE { + return errors.New("tls: no GREASE extension found") + } + + serverExtensions.serverNameAck = c.config.Bugs.SendServerNameAck + + return nil +} + +// checkForResumption returns true if we should perform resumption on this connection. +func (hs *serverHandshakeState) checkForResumption() bool { + c := hs.c + + ticket := hs.clientHello.sessionTicket + if len(ticket) == 0 && len(hs.clientHello.pskIdentities) > 0 && c.config.Bugs.AcceptAnySession { + ticket = hs.clientHello.pskIdentities[0].ticket + } + if len(ticket) > 0 { + if c.config.SessionTicketsDisabled { + return false + } + + var ok bool + if hs.sessionState, ok = c.decryptTicket(ticket); !ok { + return false + } + } else { + if c.config.ServerSessionCache == nil { + return false + } + + var ok bool + sessionId := string(hs.clientHello.sessionId) + if hs.sessionState, ok = c.config.ServerSessionCache.Get(sessionId); !ok { + return false + } + } + + if c.config.Bugs.AcceptAnySession { + // Replace the cipher suite with one known to work, to test + // cross-version resumption attempts. + hs.sessionState.cipherSuite = TLS_RSA_WITH_AES_128_CBC_SHA + } else { + // Never resume a session for a different SSL version. + if c.vers != hs.sessionState.vers { + return false + } + + cipherSuiteOk := false + // Check that the client is still offering the ciphersuite in the session. + for _, id := range hs.clientHello.cipherSuites { + if id == hs.sessionState.cipherSuite { + cipherSuiteOk = true + break + } + } + if !cipherSuiteOk { + return false + } + } + + // Check that we also support the ciphersuite from the session. + hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), c.vers, hs.ellipticOk, hs.ecdsaOk) + + if hs.suite == nil { + return false + } + + sessionHasClientCerts := len(hs.sessionState.certificates) != 0 + needClientCerts := c.config.ClientAuth == RequireAnyClientCert || c.config.ClientAuth == RequireAndVerifyClientCert + if needClientCerts && !sessionHasClientCerts { + return false + } + if sessionHasClientCerts && c.config.ClientAuth == NoClientCert { + return false + } + + return true +} + +func (hs *serverHandshakeState) doResumeHandshake() error { + c := hs.c + + hs.hello.cipherSuite = hs.suite.id + if c.config.Bugs.SendCipherSuite != 0 { + hs.hello.cipherSuite = c.config.Bugs.SendCipherSuite + } + // We echo the client's session ID in the ServerHello to let it know + // that we're doing a resumption. + hs.hello.sessionId = hs.clientHello.sessionId + hs.hello.extensions.ticketSupported = c.config.Bugs.RenewTicketOnResume + + if c.config.Bugs.SendSCTListOnResume != nil { + hs.hello.extensions.sctList = c.config.Bugs.SendSCTListOnResume + } + + if c.config.Bugs.SendOCSPResponseOnResume != nil { + // There is no way, syntactically, to send an OCSP response on a + // resumption handshake. + hs.hello.extensions.ocspStapling = true + } + + hs.finishedHash = newFinishedHash(c.vers, hs.suite) + hs.finishedHash.discardHandshakeBuffer() + hs.writeClientHash(hs.clientHello.marshal()) + hs.writeServerHash(hs.hello.marshal()) + + c.writeRecord(recordTypeHandshake, hs.hello.marshal()) + + if len(hs.sessionState.certificates) > 0 { + if _, err := hs.processCertsFromClient(hs.sessionState.certificates); err != nil { + return err + } + } + + hs.masterSecret = hs.sessionState.masterSecret + c.extendedMasterSecret = hs.sessionState.extendedMasterSecret + + return nil +} + +func (hs *serverHandshakeState) doFullHandshake() error { + config := hs.c.config + c := hs.c + + isPSK := hs.suite.flags&suitePSK != 0 + if !isPSK && hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 { + hs.hello.extensions.ocspStapling = true + } + + if hs.clientHello.sctListSupported && len(hs.cert.SignedCertificateTimestampList) > 0 { + hs.hello.extensions.sctList = hs.cert.SignedCertificateTimestampList + } + + hs.hello.extensions.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled && c.vers > VersionSSL30 + hs.hello.cipherSuite = hs.suite.id + if config.Bugs.SendCipherSuite != 0 { + hs.hello.cipherSuite = config.Bugs.SendCipherSuite + } + c.extendedMasterSecret = hs.hello.extensions.extendedMasterSecret + + // Generate a session ID if we're to save the session. + if !hs.hello.extensions.ticketSupported && config.ServerSessionCache != nil { + hs.hello.sessionId = make([]byte, 32) + if _, err := io.ReadFull(config.rand(), hs.hello.sessionId); err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: short read from Rand: " + err.Error()) + } + } + + hs.finishedHash = newFinishedHash(c.vers, hs.suite) + hs.writeClientHash(hs.clientHello.marshal()) + hs.writeServerHash(hs.hello.marshal()) + + if config.Bugs.SendSNIWarningAlert { + c.SendAlert(alertLevelWarning, alertUnrecognizedName) + } + + c.writeRecord(recordTypeHandshake, hs.hello.marshal()) + + if !isPSK { + certMsg := new(certificateMsg) + if !config.Bugs.EmptyCertificateList { + for _, certData := range hs.cert.Certificate { + certMsg.certificates = append(certMsg.certificates, certificateEntry{ + data: certData, + }) + } + } + if !config.Bugs.UnauthenticatedECDH { + certMsgBytes := certMsg.marshal() + hs.writeServerHash(certMsgBytes) + c.writeRecord(recordTypeHandshake, certMsgBytes) + } + } + + if hs.hello.extensions.ocspStapling && !c.config.Bugs.SkipCertificateStatus { + certStatus := new(certificateStatusMsg) + certStatus.statusType = statusTypeOCSP + certStatus.response = hs.cert.OCSPStaple + hs.writeServerHash(certStatus.marshal()) + c.writeRecord(recordTypeHandshake, certStatus.marshal()) + } + + keyAgreement := hs.suite.ka(c.vers) + skx, err := keyAgreement.generateServerKeyExchange(config, hs.cert, hs.clientHello, hs.hello) + if err != nil { + c.sendAlert(alertHandshakeFailure) + return err + } + if ecdhe, ok := keyAgreement.(*ecdheKeyAgreement); ok { + c.curveID = ecdhe.curveID + } + if skx != nil && !config.Bugs.SkipServerKeyExchange { + hs.writeServerHash(skx.marshal()) + c.writeRecord(recordTypeHandshake, skx.marshal()) + } + + if config.ClientAuth >= RequestClientCert { + // Request a client certificate + certReq := &certificateRequestMsg{ + certificateTypes: config.ClientCertificateTypes, + } + if certReq.certificateTypes == nil { + certReq.certificateTypes = []byte{ + byte(CertTypeRSASign), + byte(CertTypeECDSASign), + } + } + if c.vers >= VersionTLS12 { + certReq.hasSignatureAlgorithm = true + if !config.Bugs.NoSignatureAlgorithms { + certReq.signatureAlgorithms = config.verifySignatureAlgorithms() + } + } + + // An empty list of certificateAuthorities signals to + // the client that it may send any certificate in response + // to our request. When we know the CAs we trust, then + // we can send them down, so that the client can choose + // an appropriate certificate to give to us. + if config.ClientCAs != nil { + certReq.certificateAuthorities = config.ClientCAs.Subjects() + } + hs.writeServerHash(certReq.marshal()) + c.writeRecord(recordTypeHandshake, certReq.marshal()) + } + + helloDone := new(serverHelloDoneMsg) + hs.writeServerHash(helloDone.marshal()) + c.writeRecord(recordTypeHandshake, helloDone.marshal()) + c.flushHandshake() + + var pub crypto.PublicKey // public key for client auth, if any + + if err := c.simulatePacketLoss(nil); err != nil { + return err + } + msg, err := c.readHandshake() + if err != nil { + return err + } + + var ok bool + // If we requested a client certificate, then the client must send a + // certificate message, even if it's empty. + if config.ClientAuth >= RequestClientCert { + var certMsg *certificateMsg + var certificates [][]byte + if certMsg, ok = msg.(*certificateMsg); ok { + if c.vers == VersionSSL30 && len(certMsg.certificates) == 0 { + return errors.New("tls: empty certificate message in SSL 3.0") + } + + hs.writeClientHash(certMsg.marshal()) + for _, cert := range certMsg.certificates { + certificates = append(certificates, cert.data) + } + } else if c.vers == VersionSSL30 { + // In SSL 3.0, no certificate is signaled by a warning + // alert which we translate to ssl3NoCertificateMsg. + if _, ok := msg.(*ssl3NoCertificateMsg); !ok { + return errors.New("tls: client provided neither a certificate nor no_certificate warning alert") + } + } else { + // In TLS, the Certificate message is required. In SSL + // 3.0, the peer skips it when sending no certificates. + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + + if len(certificates) == 0 { + // The client didn't actually send a certificate + switch config.ClientAuth { + case RequireAnyClientCert, RequireAndVerifyClientCert: + c.sendAlert(alertBadCertificate) + return errors.New("tls: client didn't provide a certificate") + } + } + + pub, err = hs.processCertsFromClient(certificates) + if err != nil { + return err + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + // Get client key exchange + ckx, ok := msg.(*clientKeyExchangeMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(ckx, msg) + } + hs.writeClientHash(ckx.marshal()) + + preMasterSecret, err := keyAgreement.processClientKeyExchange(config, hs.cert, ckx, c.vers) + if err != nil { + c.sendAlert(alertHandshakeFailure) + return err + } + if c.extendedMasterSecret { + hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash) + } else { + if c.config.Bugs.RequireExtendedMasterSecret { + return errors.New("tls: extended master secret required but not supported by peer") + } + hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random) + } + + // If we received a client cert in response to our certificate request message, + // the client will send us a certificateVerifyMsg immediately after the + // clientKeyExchangeMsg. This message is a digest of all preceding + // handshake-layer messages that is signed using the private key corresponding + // to the client's certificate. This allows us to verify that the client is in + // possession of the private key of the certificate. + if len(c.peerCertificates) > 0 { + msg, err = c.readHandshake() + if err != nil { + return err + } + certVerify, ok := msg.(*certificateVerifyMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certVerify, msg) + } + + // Determine the signature type. + var sigAlg signatureAlgorithm + if certVerify.hasSignatureAlgorithm { + sigAlg = certVerify.signatureAlgorithm + c.peerSignatureAlgorithm = sigAlg + } + + if c.vers > VersionSSL30 { + err = verifyMessage(c.vers, pub, c.config, sigAlg, hs.finishedHash.buffer, certVerify.signature) + } else { + // SSL 3.0's client certificate construction is + // incompatible with signatureAlgorithm. + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + err = errors.New("unsupported key type for client certificate") + } else { + digest := hs.finishedHash.hashForClientCertificateSSL3(hs.masterSecret) + err = rsa.VerifyPKCS1v15(rsaPub, crypto.MD5SHA1, digest, certVerify.signature) + } + } + if err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("could not validate signature of connection nonces: " + err.Error()) + } + + hs.writeClientHash(certVerify.marshal()) + } + + hs.finishedHash.discardHandshakeBuffer() + + return nil +} + +func (hs *serverHandshakeState) establishKeys() error { + c := hs.c + + clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := + keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen(c.vers)) + + var clientCipher, serverCipher interface{} + var clientHash, serverHash macFunction + + if hs.suite.aead == nil { + clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */) + clientHash = hs.suite.mac(c.vers, clientMAC) + serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */) + serverHash = hs.suite.mac(c.vers, serverMAC) + } else { + clientCipher = hs.suite.aead(c.vers, clientKey, clientIV) + serverCipher = hs.suite.aead(c.vers, serverKey, serverIV) + } + + c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) + c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) + + return nil +} + +func (hs *serverHandshakeState) readFinished(out []byte, isResume bool) error { + c := hs.c + + c.readRecord(recordTypeChangeCipherSpec) + if err := c.in.error(); err != nil { + return err + } + + if hs.hello.extensions.nextProtoNeg { + msg, err := c.readHandshake() + if err != nil { + return err + } + nextProto, ok := msg.(*nextProtoMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(nextProto, msg) + } + hs.writeClientHash(nextProto.marshal()) + c.clientProtocol = nextProto.proto + } + + if hs.hello.extensions.channelIDRequested { + msg, err := c.readHandshake() + if err != nil { + return err + } + channelIDMsg, ok := msg.(*channelIDMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(channelIDMsg, msg) + } + var resumeHash []byte + if isResume { + resumeHash = hs.sessionState.handshakeHash + } + channelID, err := verifyChannelIDMessage(channelIDMsg, hs.finishedHash.hashForChannelID(resumeHash)) + if err != nil { + return err + } + c.channelID = channelID + + hs.writeClientHash(channelIDMsg.marshal()) + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + clientFinished, ok := msg.(*finishedMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(clientFinished, msg) + } + + verify := hs.finishedHash.clientSum(hs.masterSecret) + if len(verify) != len(clientFinished.verifyData) || + subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: client's Finished message is incorrect") + } + c.clientVerify = append(c.clientVerify[:0], clientFinished.verifyData...) + copy(out, clientFinished.verifyData) + + hs.writeClientHash(clientFinished.marshal()) + return nil +} + +func (hs *serverHandshakeState) sendSessionTicket() error { + c := hs.c + state := sessionState{ + vers: c.vers, + cipherSuite: hs.suite.id, + masterSecret: hs.masterSecret, + certificates: hs.certsFromClient, + handshakeHash: hs.finishedHash.Sum(), + } + + if !hs.hello.extensions.ticketSupported || hs.c.config.Bugs.SkipNewSessionTicket { + if c.config.ServerSessionCache != nil && len(hs.hello.sessionId) != 0 { + c.config.ServerSessionCache.Put(string(hs.hello.sessionId), &state) + } + return nil + } + + m := new(newSessionTicketMsg) + if c.config.Bugs.SendTicketLifetime != 0 { + m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second) + } + + if !c.config.Bugs.SendEmptySessionTicket { + var err error + m.ticket, err = c.encryptTicket(&state) + if err != nil { + return err + } + } + + hs.writeServerHash(m.marshal()) + c.writeRecord(recordTypeHandshake, m.marshal()) + + return nil +} + +func (hs *serverHandshakeState) sendFinished(out []byte) error { + c := hs.c + + finished := new(finishedMsg) + finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) + copy(out, finished.verifyData) + if c.config.Bugs.BadFinished { + finished.verifyData[0]++ + } + c.serverVerify = append(c.serverVerify[:0], finished.verifyData...) + hs.finishedBytes = finished.marshal() + hs.writeServerHash(hs.finishedBytes) + postCCSBytes := hs.finishedBytes + + if c.config.Bugs.FragmentAcrossChangeCipherSpec { + c.writeRecord(recordTypeHandshake, postCCSBytes[:5]) + postCCSBytes = postCCSBytes[5:] + } else if c.config.Bugs.SendUnencryptedFinished { + c.writeRecord(recordTypeHandshake, postCCSBytes) + postCCSBytes = nil + } + c.flushHandshake() + + if !c.config.Bugs.SkipChangeCipherSpec { + ccs := []byte{1} + if c.config.Bugs.BadChangeCipherSpec != nil { + ccs = c.config.Bugs.BadChangeCipherSpec + } + c.writeRecord(recordTypeChangeCipherSpec, ccs) + } + + if c.config.Bugs.AppDataAfterChangeCipherSpec != nil { + c.writeRecord(recordTypeApplicationData, c.config.Bugs.AppDataAfterChangeCipherSpec) + } + if c.config.Bugs.AlertAfterChangeCipherSpec != 0 { + c.sendAlert(c.config.Bugs.AlertAfterChangeCipherSpec) + return errors.New("tls: simulating post-CCS alert") + } + + if !c.config.Bugs.SkipFinished && len(postCCSBytes) > 0 { + c.writeRecord(recordTypeHandshake, postCCSBytes) + if c.config.Bugs.SendExtraFinished { + c.writeRecord(recordTypeHandshake, finished.marshal()) + } + + if !c.config.Bugs.PackHelloRequestWithFinished { + // Defer flushing until renegotiation. + c.flushHandshake() + } + } + + c.cipherSuite = hs.suite + + return nil +} + +// processCertsFromClient takes a chain of client certificates either from a +// Certificates message or from a sessionState and verifies them. It returns +// the public key of the leaf certificate. +func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) { + c := hs.c + + hs.certsFromClient = certificates + certs := make([]*x509.Certificate, len(certificates)) + var err error + for i, asn1Data := range certificates { + if certs[i], err = x509.ParseCertificate(asn1Data); err != nil { + c.sendAlert(alertBadCertificate) + return nil, errors.New("tls: failed to parse client certificate: " + err.Error()) + } + } + + if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { + opts := x509.VerifyOptions{ + Roots: c.config.ClientCAs, + CurrentTime: c.config.time(), + Intermediates: x509.NewCertPool(), + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + } + + for _, cert := range certs[1:] { + opts.Intermediates.AddCert(cert) + } + + chains, err := certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return nil, errors.New("tls: failed to verify client's certificate: " + err.Error()) + } + + ok := false + for _, ku := range certs[0].ExtKeyUsage { + if ku == x509.ExtKeyUsageClientAuth { + ok = true + break + } + } + if !ok { + c.sendAlert(alertHandshakeFailure) + return nil, errors.New("tls: client's certificate's extended key usage doesn't permit it to be used for client authentication") + } + + c.verifiedChains = chains + } + + if len(certs) > 0 { + var pub crypto.PublicKey + switch key := certs[0].PublicKey.(type) { + case *ecdsa.PublicKey, *rsa.PublicKey: + pub = key + default: + c.sendAlert(alertUnsupportedCertificate) + return nil, fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey) + } + c.peerCertificates = certs + return pub, nil + } + + return nil, nil +} + +func verifyChannelIDMessage(channelIDMsg *channelIDMsg, channelIDHash []byte) (*ecdsa.PublicKey, error) { + x := new(big.Int).SetBytes(channelIDMsg.channelID[0:32]) + y := new(big.Int).SetBytes(channelIDMsg.channelID[32:64]) + r := new(big.Int).SetBytes(channelIDMsg.channelID[64:96]) + s := new(big.Int).SetBytes(channelIDMsg.channelID[96:128]) + if !elliptic.P256().IsOnCurve(x, y) { + return nil, errors.New("tls: invalid channel ID public key") + } + channelID := &ecdsa.PublicKey{elliptic.P256(), x, y} + if !ecdsa.Verify(channelID, channelIDHash, r, s) { + return nil, errors.New("tls: invalid channel ID signature") + } + return channelID, nil +} + +func (hs *serverHandshakeState) writeServerHash(msg []byte) { + // writeServerHash is called before writeRecord. + hs.writeHash(msg, hs.c.sendHandshakeSeq) +} + +func (hs *serverHandshakeState) writeClientHash(msg []byte) { + // writeClientHash is called after readHandshake. + hs.writeHash(msg, hs.c.recvHandshakeSeq-1) +} + +func (hs *serverHandshakeState) writeHash(msg []byte, seqno uint16) { + if hs.c.isDTLS { + // This is somewhat hacky. DTLS hashes a slightly different format. + // First, the TLS header. + hs.finishedHash.Write(msg[:4]) + // Then the sequence number and reassembled fragment offset (always 0). + hs.finishedHash.Write([]byte{byte(seqno >> 8), byte(seqno), 0, 0, 0}) + // Then the reassembled fragment (always equal to the message length). + hs.finishedHash.Write(msg[1:4]) + // And then the message body. + hs.finishedHash.Write(msg[4:]) + } else { + hs.finishedHash.Write(msg) + } +} + +// tryCipherSuite returns a cipherSuite with the given id if that cipher suite +// is acceptable to use. +func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16, ellipticOk, ecdsaOk bool) *cipherSuite { + for _, supported := range supportedCipherSuites { + if id == supported { + var candidate *cipherSuite + + for _, s := range cipherSuites { + if s.id == id { + candidate = s + break + } + } + if candidate == nil { + continue + } + + // Don't select a ciphersuite which we can't + // support for this client. + if version >= VersionTLS13 || candidate.flags&suiteTLS13 != 0 { + if version < VersionTLS13 || candidate.flags&suiteTLS13 == 0 { + continue + } + return candidate + } + if (candidate.flags&suiteECDHE != 0) && !ellipticOk { + continue + } + if (candidate.flags&suiteECDSA != 0) != ecdsaOk { + continue + } + if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 { + continue + } + if c.isDTLS && candidate.flags&suiteNoDTLS != 0 { + continue + } + return candidate + } + } + + return nil +} + +func isTLS12Cipher(id uint16) bool { + for _, cipher := range cipherSuites { + if cipher.id != id { + continue + } + return cipher.flags&suiteTLS12 != 0 + } + // Unknown cipher. + return false +} + +func isGREASEValue(val uint16) bool { + return val&0x0f0f == 0x0a0a && val&0xff == val>>8 +} + +func verifyPSKBinder(clientHello *clientHelloMsg, sessionState *sessionState, binderToVerify, transcript []byte) error { + binderLen := 2 + for _, binder := range clientHello.pskBinders { + binderLen += 1 + len(binder) + } + + truncatedHello := clientHello.marshal() + truncatedHello = truncatedHello[:len(truncatedHello)-binderLen] + pskCipherSuite := cipherSuiteFromID(sessionState.cipherSuite) + if pskCipherSuite == nil { + return errors.New("tls: Unknown cipher suite for PSK in session") + } + + binder := computePSKBinder(sessionState.masterSecret, resumptionPSKBinderLabel, pskCipherSuite, transcript, truncatedHello) + if !bytes.Equal(binder, binderToVerify) { + return errors.New("tls: PSK binder does not verify") + } + + return nil +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/hkdf.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/hkdf.go new file mode 100644 index 000000000..c60e4ccf9 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/hkdf.go @@ -0,0 +1,57 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "crypto/hmac" + "hash" +) + +// hkdfExtract implements HKDF-Extract from RFC 5869. +func hkdfExtract(hash func() hash.Hash, salt, ikm []byte) []byte { + if salt == nil { + salt = make([]byte, hash().Size()) + } + hmac := hmac.New(hash, salt) + hmac.Write(ikm) + return hmac.Sum(nil) +} + +// hkdfExpand implements HKDF-Expand from RFC 5869. +func hkdfExpand(hash func() hash.Hash, prk, info []byte, length int) []byte { + hashSize := hash().Size() + if length > 255*hashSize { + panic("hkdfExpand: length too long") + } + if len(prk) < hashSize { + panic("hkdfExpand: prk too short") + } + var lastBlock []byte + counter := byte(0) + okm := make([]byte, length) + hmac := hmac.New(hash, prk) + for length > 0 { + hmac.Reset() + counter++ + hmac.Write(lastBlock) + hmac.Write(info) + hmac.Write([]byte{counter}) + block := hmac.Sum(nil) + lastBlock = block + copy(okm[(int(counter)-1)*hashSize:], block) + length -= hashSize + } + return okm +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/hkdf_test.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/hkdf_test.go new file mode 100644 index 000000000..4e6958ea9 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/hkdf_test.go @@ -0,0 +1,101 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "bytes" + "crypto" + _ "crypto/sha1" + _ "crypto/sha256" + "testing" +) + +var hkdfTests = []struct { + hash crypto.Hash + ikm, salt, info, prk, okm []byte +}{ + { + crypto.SHA256, + []byte{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}, + []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}, + []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}, + []byte{0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5}, + []byte{0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65}, + }, + { + crypto.SHA256, + []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}, + []byte{0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf}, + []byte{0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}, + []byte{0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44}, + []byte{0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87}, + }, + { + crypto.SHA256, + []byte{ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}, + []byte{}, + []byte{}, + []byte{0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04}, + []byte{0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8}, + }, + { + crypto.SHA1, + []byte{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}, + []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}, + []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}, + []byte{0x9b, 0x6c, 0x18, 0xc4, 0x32, 0xa7, 0xbf, 0x8f, 0x0e, 0x71, 0xc8, 0xeb, 0x88, 0xf4, 0xb3, 0x0b, 0xaa, 0x2b, 0xa2, 0x43}, + []byte{0x08, 0x5a, 0x01, 0xea, 0x1b, 0x10, 0xf3, 0x69, 0x33, 0x06, 0x8b, 0x56, 0xef, 0xa5, 0xad, 0x81, 0xa4, 0xf1, 0x4b, 0x82, 0x2f, 0x5b, 0x09, 0x15, 0x68, 0xa9, 0xcd, 0xd4, 0xf1, 0x55, 0xfd, 0xa2, 0xc2, 0x2e, 0x42, 0x24, 0x78, 0xd3, 0x05, 0xf3, 0xf8, 0x96}, + }, + { + crypto.SHA1, + []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f}, + []byte{0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf}, + []byte{0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}, + []byte{0x8a, 0xda, 0xe0, 0x9a, 0x2a, 0x30, 0x70, 0x59, 0x47, 0x8d, 0x30, 0x9b, 0x26, 0xc4, 0x11, 0x5a, 0x22, 0x4c, 0xfa, 0xf6}, + []byte{0x0b, 0xd7, 0x70, 0xa7, 0x4d, 0x11, 0x60, 0xf7, 0xc9, 0xf1, 0x2c, 0xd5, 0x91, 0x2a, 0x06, 0xeb, 0xff, 0x6a, 0xdc, 0xae, 0x89, 0x9d, 0x92, 0x19, 0x1f, 0xe4, 0x30, 0x56, 0x73, 0xba, 0x2f, 0xfe, 0x8f, 0xa3, 0xf1, 0xa4, 0xe5, 0xad, 0x79, 0xf3, 0xf3, 0x34, 0xb3, 0xb2, 0x02, 0xb2, 0x17, 0x3c, 0x48, 0x6e, 0xa3, 0x7c, 0xe3, 0xd3, 0x97, 0xed, 0x03, 0x4c, 0x7f, 0x9d, 0xfe, 0xb1, 0x5c, 0x5e, 0x92, 0x73, 0x36, 0xd0, 0x44, 0x1f, 0x4c, 0x43, 0x00, 0xe2, 0xcf, 0xf0, 0xd0, 0x90, 0x0b, 0x52, 0xd3, 0xb4}, + }, + { + crypto.SHA1, + []byte{0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}, + []byte{}, + []byte{}, + []byte{0xda, 0x8c, 0x8a, 0x73, 0xc7, 0xfa, 0x77, 0x28, 0x8e, 0xc6, 0xf5, 0xe7, 0xc2, 0x97, 0x78, 0x6a, 0xa0, 0xd3, 0x2d, 0x01}, + []byte{0x0a, 0xc1, 0xaf, 0x70, 0x02, 0xb3, 0xd7, 0x61, 0xd1, 0xe5, 0x52, 0x98, 0xda, 0x9d, 0x05, 0x06, 0xb9, 0xae, 0x52, 0x05, 0x72, 0x20, 0xa3, 0x06, 0xe0, 0x7b, 0x6b, 0x87, 0xe8, 0xdf, 0x21, 0xd0, 0xea, 0x00, 0x03, 0x3d, 0xe0, 0x39, 0x84, 0xd3, 0x49, 0x18}, + }, + { + crypto.SHA1, + []byte{0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c}, + []byte{}, + []byte{}, + []byte{0x2a, 0xdc, 0xca, 0xda, 0x18, 0x77, 0x9e, 0x7c, 0x20, 0x77, 0xad, 0x2e, 0xb1, 0x9d, 0x3f, 0x3e, 0x73, 0x13, 0x85, 0xdd}, + []byte{0x2c, 0x91, 0x11, 0x72, 0x04, 0xd7, 0x45, 0xf3, 0x50, 0x0d, 0x63, 0x6a, 0x62, 0xf6, 0x4f, 0x0a, 0xb3, 0xba, 0xe5, 0x48, 0xaa, 0x53, 0xd4, 0x23, 0xb0, 0xd1, 0xf2, 0x7e, 0xbb, 0xa6, 0xf5, 0xe5, 0x67, 0x3a, 0x08, 0x1d, 0x70, 0xcc, 0xe7, 0xac, 0xfc, 0x48}, + }, +} + +func TestHKDF(t *testing.T) { + for i, tt := range hkdfTests { + prk := hkdfExtract(tt.hash.New, tt.salt, tt.ikm) + if !bytes.Equal(prk, tt.prk) { + t.Errorf("%d. got hkdfExtract(%x, %x) = %x; wanted %x", i+1, tt.salt, tt.ikm, prk, tt.prk) + } + + okm := hkdfExpand(tt.hash.New, tt.prk, tt.info, len(tt.okm)) + if !bytes.Equal(okm, tt.okm) { + t.Errorf("%d. got hkdfExpand(%x, %x, %d) = %x; wanted %x", i+1, tt.prk, tt.info, len(tt.okm), okm, tt.okm) + } + } +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/key.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/key.pem new file mode 100644 index 000000000..aeba1865d --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC6C9qEGRIBQXV8 +Lj29vVu+U+tyXzSSinWIumK5ijPhCm3DLnv4RayxkFwemtnkGRZ/o94ZnsXkBfU/ +IlsYdkuq8wK9WI/ql3gwWjH+KARIhIQcSLGiJcLN6kGuG2nlRBKMcPgPiEq2B0yB +XFf4tG3CBbeae7+8G7uvOmv8NLyKj32neWpnUCTL5o2VwyPoxjLxT5gUR69v9XSV +Fj2irCZbsEedeKSb++LqyMhLfnRTzNv+ZHNh4izZHrktR25MvnT5QyBq32hx7AjZ +2/xo70OmH7w10a2DwsVjJNMdxTEmgyvU9M6CeYRPX1Ykfg+sXCTtkTVAlBDUviIq +Y95CKy25AgMBAAECggEAHPvvxRiqx2tNRFVn5QF1I4erbJwMcrADc5OmAcXYIz0e +sIOzaJBiQR9+Wn5BZ9nIuYXr+g3UQpvzAyz1CDCVxUIqsRj1AtUqMk4675+IW0vZ +0RY6Jkq/uJjANsGqk78xLJQE8VaIXSdx8c1THznsx4dgfT6+Ni4T5U6yuA33OZaw +4NdYZYtEkqNiqK6VYe4mAxxVh5qscihVVMGkBVqJNiiEotctm1lph8ow+7o8ggXO +W9xm+RHHPcH7Epx7hjkb/helANcYOK950W5/R+2zWV9R6kxo6R+/hfGFFmCvl4k5 ++i8Y0IlEv3fze1E0Lwyf379i3C/cKcuaE5gwR54BAQKBgQDxlsNy9M37HgguglHt +8W+cuPNtxNjFCWIjNR9dSvdr1Oi28Z1AY+BBPSv6UBKnT5PpOFjqxfMY/j/zoKdI +aYX1phgeQHXcHrB1pS8yoaF/pTJSN2Yb8v9kl/Ch1yeYXaNVGmeBLkH9H6wIcUxD +Mas1i8VUzshzhcluCNGoJj9wUQKBgQDFJOoWncssfWCrsuDWEoeU71Zh3+bD96GF +s29CdIbHpcbxhWYjA9RM8yxbGPopexzoGcV1HX6j8E1s0xfYZJV23rxoM9Zj9l5D +mZAJQPxYXIdu3h4PslhZLd3p+DEHjbsLC/avk3M4iZim1FMPBJMswKSL23ysqXoY +/ynor+W06QKBgHYeu6M6NHgCYAe1ai+Hq4WaHFNgOohkJRqHv7USkVSkvb+s9LDl +5GChcx4pBmXNj8ko5rirXkerEEOjGgdaqMfJlOM9qyKb0rVCtYfw5RCPCcKPGZqy +vdJGQ74tf0uNBO34QgE0R8lmMevS0XHNGCPPGgV0MSfikvD82N15De1xAoGAbsZM +RsMJfAlDPZc4oPEuf/BwMHTYPTsy5map2MSTSzGKdQHJH1myfD6TqOiDALXtyzlX +63PUShfn2YNPvcbe+Tk00rR1/htcYk2yUpDSenAbpZ9ncth6rjmInURZgG4SMKXb +SlLnBljCjtN1jFW8wQPKMc/14SslsVAHY3ka8KkCgYB58QNT1YfH3jS62+mT2pXq +qLjLqvsD742VYnFoHR+HBOnN8ry0dda4lgwM106L5FgSg9DOZvASZ+QGFk+QVQv+ +c77ASWpuhmBmamZCrwZXrq9Xc92RDPkKFqnP9MVv06hYKNp0moSdM8dIaM6uSows +/r/aDs4oudubz26o5GDKmA== +-----END PRIVATE KEY----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/key_agreement.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/key_agreement.go new file mode 100644 index 000000000..8aa91186d --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/key_agreement.go @@ -0,0 +1,853 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/subtle" + "crypto/x509" + "errors" + "fmt" + "io" + "math/big" + + "./curve25519" +) + +type keyType int + +const ( + keyTypeRSA keyType = iota + 1 + keyTypeECDSA +) + +var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message") +var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message") + +// rsaKeyAgreement implements the standard TLS key agreement where the client +// encrypts the pre-master secret to the server's public key. +type rsaKeyAgreement struct { + version uint16 + clientVersion uint16 + exportKey *rsa.PrivateKey +} + +func (ka *rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + // Save the client version for comparison later. + ka.clientVersion = clientHello.vers + + if !config.Bugs.RSAEphemeralKey { + return nil, nil + } + + // Generate an ephemeral RSA key to use instead of the real + // one, as in RSA_EXPORT. + key, err := rsa.GenerateKey(config.rand(), 512) + if err != nil { + return nil, err + } + ka.exportKey = key + + modulus := key.N.Bytes() + exponent := big.NewInt(int64(key.E)).Bytes() + serverRSAParams := make([]byte, 0, 2+len(modulus)+2+len(exponent)) + serverRSAParams = append(serverRSAParams, byte(len(modulus)>>8), byte(len(modulus))) + serverRSAParams = append(serverRSAParams, modulus...) + serverRSAParams = append(serverRSAParams, byte(len(exponent)>>8), byte(len(exponent))) + serverRSAParams = append(serverRSAParams, exponent...) + + var sigAlg signatureAlgorithm + if ka.version >= VersionTLS12 { + sigAlg, err = selectSignatureAlgorithm(ka.version, cert.PrivateKey, config, clientHello.signatureAlgorithms) + if err != nil { + return nil, err + } + } + + sig, err := signMessage(ka.version, cert.PrivateKey, config, sigAlg, serverRSAParams) + if err != nil { + return nil, errors.New("failed to sign RSA parameters: " + err.Error()) + } + + skx := new(serverKeyExchangeMsg) + sigAlgsLen := 0 + if ka.version >= VersionTLS12 { + sigAlgsLen = 2 + } + skx.key = make([]byte, len(serverRSAParams)+sigAlgsLen+2+len(sig)) + copy(skx.key, serverRSAParams) + k := skx.key[len(serverRSAParams):] + if ka.version >= VersionTLS12 { + k[0] = byte(sigAlg >> 8) + k[1] = byte(sigAlg) + k = k[2:] + } + k[0] = byte(len(sig) >> 8) + k[1] = byte(len(sig)) + copy(k[2:], sig) + + return skx, nil +} + +func (ka *rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + preMasterSecret := make([]byte, 48) + _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) + if err != nil { + return nil, err + } + + if len(ckx.ciphertext) < 2 { + return nil, errClientKeyExchange + } + + ciphertext := ckx.ciphertext + if version != VersionSSL30 { + ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) + if ciphertextLen != len(ckx.ciphertext)-2 { + return nil, errClientKeyExchange + } + ciphertext = ckx.ciphertext[2:] + } + + key := cert.PrivateKey.(*rsa.PrivateKey) + if ka.exportKey != nil { + key = ka.exportKey + } + err = rsa.DecryptPKCS1v15SessionKey(config.rand(), key, ciphertext, preMasterSecret) + if err != nil { + return nil, err + } + // This check should be done in constant-time, but this is a testing + // implementation. See the discussion at the end of section 7.4.7.1 of + // RFC 4346. + vers := uint16(preMasterSecret[0])<<8 | uint16(preMasterSecret[1]) + if ka.clientVersion != vers { + return nil, fmt.Errorf("tls: invalid version in RSA premaster (got %04x, wanted %04x)", vers, ka.clientVersion) + } + return preMasterSecret, nil +} + +func (ka *rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + return errors.New("tls: unexpected ServerKeyExchange") +} + +func (ka *rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + bad := config.Bugs.BadRSAClientKeyExchange + preMasterSecret := make([]byte, 48) + vers := clientHello.vers + if bad == RSABadValueWrongVersion { + vers ^= 1 + } + preMasterSecret[0] = byte(vers >> 8) + preMasterSecret[1] = byte(vers) + _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) + if err != nil { + return nil, nil, err + } + + sentPreMasterSecret := preMasterSecret + if bad == RSABadValueTooLong { + sentPreMasterSecret = make([]byte, len(sentPreMasterSecret)+1) + copy(sentPreMasterSecret, preMasterSecret) + } else if bad == RSABadValueTooShort { + sentPreMasterSecret = sentPreMasterSecret[:len(sentPreMasterSecret)-1] + } + + encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), sentPreMasterSecret) + if err != nil { + return nil, nil, err + } + if bad == RSABadValueCorrupt { + encrypted[len(encrypted)-1] ^= 1 + // Clear the high byte to ensure |encrypted| is still below the RSA modulus. + encrypted[0] = 0 + } + ckx := new(clientKeyExchangeMsg) + if clientHello.vers != VersionSSL30 { + ckx.ciphertext = make([]byte, len(encrypted)+2) + ckx.ciphertext[0] = byte(len(encrypted) >> 8) + ckx.ciphertext[1] = byte(len(encrypted)) + copy(ckx.ciphertext[2:], encrypted) + } else { + ckx.ciphertext = encrypted + } + return preMasterSecret, ckx, nil +} + +func (ka *rsaKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + return 0 +} + +// A ecdhCurve is an instance of ECDH-style key agreement for TLS. +type ecdhCurve interface { + // offer generates a keypair using rand. It returns the encoded |publicKey|. + offer(rand io.Reader) (publicKey []byte, err error) + + // accept responds to the |peerKey| generated by |offer| with the acceptor's + // |publicKey|, and returns agreed-upon |preMasterSecret| to the acceptor. + accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) + + // finish returns the computed |preMasterSecret|, given the |peerKey| + // generated by |accept|. + finish(peerKey []byte) (preMasterSecret []byte, err error) +} + +// ellipticECDHCurve implements ecdhCurve with an elliptic.Curve. +type ellipticECDHCurve struct { + curve elliptic.Curve + privateKey []byte +} + +func (e *ellipticECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) { + var x, y *big.Int + e.privateKey, x, y, err = elliptic.GenerateKey(e.curve, rand) + if err != nil { + return nil, err + } + return elliptic.Marshal(e.curve, x, y), nil +} + +func (e *ellipticECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) { + publicKey, err = e.offer(rand) + if err != nil { + return nil, nil, err + } + preMasterSecret, err = e.finish(peerKey) + if err != nil { + return nil, nil, err + } + return +} + +func (e *ellipticECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) { + x, y := elliptic.Unmarshal(e.curve, peerKey) + if x == nil { + return nil, errors.New("tls: invalid peer key") + } + x, _ = e.curve.ScalarMult(x, y, e.privateKey) + preMasterSecret = make([]byte, (e.curve.Params().BitSize+7)>>3) + xBytes := x.Bytes() + copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) + + return preMasterSecret, nil +} + +// x25519ECDHCurve implements ecdhCurve with X25519. +type x25519ECDHCurve struct { + privateKey [32]byte +} + +func (e *x25519ECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) { + _, err = io.ReadFull(rand, e.privateKey[:]) + if err != nil { + return + } + var out [32]byte + curve25519.ScalarBaseMult(&out, &e.privateKey) + return out[:], nil +} + +func (e *x25519ECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) { + publicKey, err = e.offer(rand) + if err != nil { + return nil, nil, err + } + preMasterSecret, err = e.finish(peerKey) + if err != nil { + return nil, nil, err + } + return +} + +func (e *x25519ECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) { + if len(peerKey) != 32 { + return nil, errors.New("tls: invalid peer key") + } + var out, peerKeyCopy [32]byte + copy(peerKeyCopy[:], peerKey) + curve25519.ScalarMult(&out, &e.privateKey, &peerKeyCopy) + + // Per RFC 7748, reject the all-zero value in constant time. + var zeros [32]byte + if subtle.ConstantTimeCompare(zeros[:], out[:]) == 1 { + return nil, errors.New("tls: X25519 value with wrong order") + } + + return out[:], nil +} + +func curveForCurveID(id CurveID) (ecdhCurve, bool) { + switch id { + case CurveP224: + return &ellipticECDHCurve{curve: elliptic.P224()}, true + case CurveP256: + return &ellipticECDHCurve{curve: elliptic.P256()}, true + case CurveP384: + return &ellipticECDHCurve{curve: elliptic.P384()}, true + case CurveP521: + return &ellipticECDHCurve{curve: elliptic.P521()}, true + case CurveX25519: + return &x25519ECDHCurve{}, true + default: + return nil, false + } + +} + +// keyAgreementAuthentication is a helper interface that specifies how +// to authenticate the ServerKeyExchange parameters. +type keyAgreementAuthentication interface { + signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) + verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error +} + +// nilKeyAgreementAuthentication does not authenticate the key +// agreement parameters. +type nilKeyAgreementAuthentication struct{} + +func (ka *nilKeyAgreementAuthentication) signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) { + skx := new(serverKeyExchangeMsg) + skx.key = params + return skx, nil +} + +func (ka *nilKeyAgreementAuthentication) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error { + return nil +} + +// signedKeyAgreement signs the ServerKeyExchange parameters with the +// server's private key. +type signedKeyAgreement struct { + keyType keyType + version uint16 + peerSignatureAlgorithm signatureAlgorithm +} + +func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) { + // The message to be signed is prepended by the randoms. + var msg []byte + msg = append(msg, clientHello.random...) + msg = append(msg, hello.random...) + msg = append(msg, params...) + + var sigAlg signatureAlgorithm + var err error + if ka.version >= VersionTLS12 { + sigAlg, err = selectSignatureAlgorithm(ka.version, cert.PrivateKey, config, clientHello.signatureAlgorithms) + if err != nil { + return nil, err + } + } + + sig, err := signMessage(ka.version, cert.PrivateKey, config, sigAlg, msg) + if err != nil { + return nil, err + } + if config.Bugs.SendSignatureAlgorithm != 0 { + sigAlg = config.Bugs.SendSignatureAlgorithm + } + + skx := new(serverKeyExchangeMsg) + if config.Bugs.UnauthenticatedECDH { + skx.key = params + } else { + sigAlgsLen := 0 + if ka.version >= VersionTLS12 { + sigAlgsLen = 2 + } + skx.key = make([]byte, len(params)+sigAlgsLen+2+len(sig)) + copy(skx.key, params) + k := skx.key[len(params):] + if ka.version >= VersionTLS12 { + k[0] = byte(sigAlg >> 8) + k[1] = byte(sigAlg) + k = k[2:] + } + k[0] = byte(len(sig) >> 8) + k[1] = byte(len(sig)) + copy(k[2:], sig) + } + + return skx, nil +} + +func (ka *signedKeyAgreement) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error { + // The peer's key must match the cipher type. + switch ka.keyType { + case keyTypeECDSA: + _, ok := cert.PublicKey.(*ecdsa.PublicKey) + if !ok { + return errors.New("tls: ECDHE ECDSA requires a ECDSA server public key") + } + case keyTypeRSA: + _, ok := cert.PublicKey.(*rsa.PublicKey) + if !ok { + return errors.New("tls: ECDHE RSA requires a RSA server public key") + } + default: + return errors.New("tls: unknown key type") + } + + // The message to be signed is prepended by the randoms. + var msg []byte + msg = append(msg, clientHello.random...) + msg = append(msg, serverHello.random...) + msg = append(msg, params...) + + var sigAlg signatureAlgorithm + if ka.version >= VersionTLS12 { + if len(sig) < 2 { + return errServerKeyExchange + } + sigAlg = signatureAlgorithm(sig[0])<<8 | signatureAlgorithm(sig[1]) + sig = sig[2:] + // Stash the signature algorithm to be extracted by the handshake. + ka.peerSignatureAlgorithm = sigAlg + } + + if len(sig) < 2 { + return errServerKeyExchange + } + sigLen := int(sig[0])<<8 | int(sig[1]) + if sigLen+2 != len(sig) { + return errServerKeyExchange + } + sig = sig[2:] + + return verifyMessage(ka.version, cert.PublicKey, config, sigAlg, msg, sig) +} + +// ecdheKeyAgreement implements a TLS key agreement where the server +// generates a ephemeral EC public/private key pair and signs it. The +// pre-master secret is then calculated using ECDH. The signature may +// either be ECDSA or RSA. +type ecdheKeyAgreement struct { + auth keyAgreementAuthentication + curve ecdhCurve + curveID CurveID + peerKey []byte +} + +func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + var curveid CurveID + preferredCurves := config.curvePreferences() + +NextCandidate: + for _, candidate := range preferredCurves { + for _, c := range clientHello.supportedCurves { + if candidate == c { + curveid = c + break NextCandidate + } + } + } + + if curveid == 0 { + return nil, errors.New("tls: no supported elliptic curves offered") + } + + var ok bool + if ka.curve, ok = curveForCurveID(curveid); !ok { + return nil, errors.New("tls: preferredCurves includes unsupported curve") + } + ka.curveID = curveid + + publicKey, err := ka.curve.offer(config.rand()) + if err != nil { + return nil, err + } + + // http://tools.ietf.org/html/rfc4492#section-5.4 + serverECDHParams := make([]byte, 1+2+1+len(publicKey)) + serverECDHParams[0] = 3 // named curve + if config.Bugs.SendCurve != 0 { + curveid = config.Bugs.SendCurve + } + serverECDHParams[1] = byte(curveid >> 8) + serverECDHParams[2] = byte(curveid) + serverECDHParams[3] = byte(len(publicKey)) + copy(serverECDHParams[4:], publicKey) + if config.Bugs.InvalidECDHPoint { + serverECDHParams[4] ^= 0xff + } + + return ka.auth.signParameters(config, cert, clientHello, hello, serverECDHParams) +} + +func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { + return nil, errClientKeyExchange + } + return ka.curve.finish(ckx.ciphertext[1:]) +} + +func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + if len(skx.key) < 4 { + return errServerKeyExchange + } + if skx.key[0] != 3 { // named curve + return errors.New("tls: server selected unsupported curve") + } + curveid := CurveID(skx.key[1])<<8 | CurveID(skx.key[2]) + ka.curveID = curveid + + var ok bool + if ka.curve, ok = curveForCurveID(curveid); !ok { + return errors.New("tls: server selected unsupported curve") + } + + publicLen := int(skx.key[3]) + if publicLen+4 > len(skx.key) { + return errServerKeyExchange + } + // Save the peer key for later. + ka.peerKey = skx.key[4 : 4+publicLen] + + // Check the signature. + serverECDHParams := skx.key[:4+publicLen] + sig := skx.key[4+publicLen:] + return ka.auth.verifyParameters(config, clientHello, serverHello, cert, serverECDHParams, sig) +} + +func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + if ka.curve == nil { + return nil, nil, errors.New("missing ServerKeyExchange message") + } + + publicKey, preMasterSecret, err := ka.curve.accept(config.rand(), ka.peerKey) + if err != nil { + return nil, nil, err + } + + ckx := new(clientKeyExchangeMsg) + ckx.ciphertext = make([]byte, 1+len(publicKey)) + ckx.ciphertext[0] = byte(len(publicKey)) + copy(ckx.ciphertext[1:], publicKey) + if config.Bugs.InvalidECDHPoint { + ckx.ciphertext[1] ^= 0xff + } + + return preMasterSecret, ckx, nil +} + +func (ka *ecdheKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + if auth, ok := ka.auth.(*signedKeyAgreement); ok { + return auth.peerSignatureAlgorithm + } + return 0 +} + +// dheRSAKeyAgreement implements a TLS key agreement where the server generates +// an ephemeral Diffie-Hellman public/private key pair and signs it. The +// pre-master secret is then calculated using Diffie-Hellman. +type dheKeyAgreement struct { + auth keyAgreementAuthentication + p, g *big.Int + yTheirs *big.Int + xOurs *big.Int +} + +func (ka *dheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + var q *big.Int + if p := config.Bugs.DHGroupPrime; p != nil { + ka.p = p + ka.g = big.NewInt(2) + q = p + } else { + // 2048-bit MODP Group with 256-bit Prime Order Subgroup (RFC + // 5114, Section 2.3) + ka.p, _ = new(big.Int).SetString("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597", 16) + ka.g, _ = new(big.Int).SetString("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659", 16) + q, _ = new(big.Int).SetString("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", 16) + } + + var err error + ka.xOurs, err = rand.Int(config.rand(), q) + if err != nil { + return nil, err + } + yOurs := new(big.Int).Exp(ka.g, ka.xOurs, ka.p) + + // http://tools.ietf.org/html/rfc5246#section-7.4.3 + pBytes := ka.p.Bytes() + gBytes := ka.g.Bytes() + yBytes := yOurs.Bytes() + serverDHParams := make([]byte, 0, 2+len(pBytes)+2+len(gBytes)+2+len(yBytes)) + serverDHParams = append(serverDHParams, byte(len(pBytes)>>8), byte(len(pBytes))) + serverDHParams = append(serverDHParams, pBytes...) + serverDHParams = append(serverDHParams, byte(len(gBytes)>>8), byte(len(gBytes))) + serverDHParams = append(serverDHParams, gBytes...) + serverDHParams = append(serverDHParams, byte(len(yBytes)>>8), byte(len(yBytes))) + serverDHParams = append(serverDHParams, yBytes...) + + return ka.auth.signParameters(config, cert, clientHello, hello, serverDHParams) +} + +func (ka *dheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + if len(ckx.ciphertext) < 2 { + return nil, errClientKeyExchange + } + yLen := (int(ckx.ciphertext[0]) << 8) | int(ckx.ciphertext[1]) + if yLen != len(ckx.ciphertext)-2 { + return nil, errClientKeyExchange + } + yTheirs := new(big.Int).SetBytes(ckx.ciphertext[2:]) + if yTheirs.Sign() <= 0 || yTheirs.Cmp(ka.p) >= 0 { + return nil, errClientKeyExchange + } + return new(big.Int).Exp(yTheirs, ka.xOurs, ka.p).Bytes(), nil +} + +func (ka *dheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + // Read dh_p + k := skx.key + if len(k) < 2 { + return errServerKeyExchange + } + pLen := (int(k[0]) << 8) | int(k[1]) + k = k[2:] + if len(k) < pLen { + return errServerKeyExchange + } + ka.p = new(big.Int).SetBytes(k[:pLen]) + k = k[pLen:] + + // Read dh_g + if len(k) < 2 { + return errServerKeyExchange + } + gLen := (int(k[0]) << 8) | int(k[1]) + k = k[2:] + if len(k) < gLen { + return errServerKeyExchange + } + ka.g = new(big.Int).SetBytes(k[:gLen]) + k = k[gLen:] + + // Read dh_Ys + if len(k) < 2 { + return errServerKeyExchange + } + yLen := (int(k[0]) << 8) | int(k[1]) + k = k[2:] + if len(k) < yLen { + return errServerKeyExchange + } + ka.yTheirs = new(big.Int).SetBytes(k[:yLen]) + k = k[yLen:] + if ka.yTheirs.Sign() <= 0 || ka.yTheirs.Cmp(ka.p) >= 0 { + return errServerKeyExchange + } + + if l := config.Bugs.RequireDHPublicValueLen; l != 0 && l != yLen { + return fmt.Errorf("RequireDHPublicValueLen set to %d, but server's public value was %d bytes on the wire and %d bytes if minimal", l, yLen, (ka.yTheirs.BitLen()+7)/8) + } + + sig := k + serverDHParams := skx.key[:len(skx.key)-len(sig)] + + return ka.auth.verifyParameters(config, clientHello, serverHello, cert, serverDHParams, sig) +} + +func (ka *dheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + if ka.p == nil || ka.g == nil || ka.yTheirs == nil { + return nil, nil, errors.New("missing ServerKeyExchange message") + } + + xOurs, err := rand.Int(config.rand(), ka.p) + if err != nil { + return nil, nil, err + } + preMasterSecret := new(big.Int).Exp(ka.yTheirs, xOurs, ka.p).Bytes() + + yOurs := new(big.Int).Exp(ka.g, xOurs, ka.p) + yBytes := yOurs.Bytes() + ckx := new(clientKeyExchangeMsg) + ckx.ciphertext = make([]byte, 2+len(yBytes)) + ckx.ciphertext[0] = byte(len(yBytes) >> 8) + ckx.ciphertext[1] = byte(len(yBytes)) + copy(ckx.ciphertext[2:], yBytes) + + return preMasterSecret, ckx, nil +} + +func (ka *dheKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + if auth, ok := ka.auth.(*signedKeyAgreement); ok { + return auth.peerSignatureAlgorithm + } + return 0 +} + +// nilKeyAgreement is a fake key agreement used to implement the plain PSK key +// exchange. +type nilKeyAgreement struct{} + +func (ka *nilKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + return nil, nil +} + +func (ka *nilKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + if len(ckx.ciphertext) != 0 { + return nil, errClientKeyExchange + } + + // Although in plain PSK, otherSecret is all zeros, the base key + // agreement does not access to the length of the pre-shared + // key. pskKeyAgreement instead interprets nil to mean to use all zeros + // of the appropriate length. + return nil, nil +} + +func (ka *nilKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + if len(skx.key) != 0 { + return errServerKeyExchange + } + return nil +} + +func (ka *nilKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + // Although in plain PSK, otherSecret is all zeros, the base key + // agreement does not access to the length of the pre-shared + // key. pskKeyAgreement instead interprets nil to mean to use all zeros + // of the appropriate length. + return nil, &clientKeyExchangeMsg{}, nil +} + +func (ka *nilKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + return 0 +} + +// makePSKPremaster formats a PSK pre-master secret based on otherSecret from +// the base key exchange and psk. +func makePSKPremaster(otherSecret, psk []byte) []byte { + out := make([]byte, 0, 2+len(otherSecret)+2+len(psk)) + out = append(out, byte(len(otherSecret)>>8), byte(len(otherSecret))) + out = append(out, otherSecret...) + out = append(out, byte(len(psk)>>8), byte(len(psk))) + out = append(out, psk...) + return out +} + +// pskKeyAgreement implements the PSK key agreement. +type pskKeyAgreement struct { + base keyAgreement + identityHint string +} + +func (ka *pskKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + // Assemble the identity hint. + bytes := make([]byte, 2+len(config.PreSharedKeyIdentity)) + bytes[0] = byte(len(config.PreSharedKeyIdentity) >> 8) + bytes[1] = byte(len(config.PreSharedKeyIdentity)) + copy(bytes[2:], []byte(config.PreSharedKeyIdentity)) + + // If there is one, append the base key agreement's + // ServerKeyExchange. + baseSkx, err := ka.base.generateServerKeyExchange(config, cert, clientHello, hello) + if err != nil { + return nil, err + } + + if baseSkx != nil { + bytes = append(bytes, baseSkx.key...) + } else if config.PreSharedKeyIdentity == "" && !config.Bugs.AlwaysSendPreSharedKeyIdentityHint { + // ServerKeyExchange is optional if the identity hint is empty + // and there would otherwise be no ServerKeyExchange. + return nil, nil + } + + skx := new(serverKeyExchangeMsg) + skx.key = bytes + return skx, nil +} + +func (ka *pskKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + // First, process the PSK identity. + if len(ckx.ciphertext) < 2 { + return nil, errClientKeyExchange + } + identityLen := (int(ckx.ciphertext[0]) << 8) | int(ckx.ciphertext[1]) + if 2+identityLen > len(ckx.ciphertext) { + return nil, errClientKeyExchange + } + identity := string(ckx.ciphertext[2 : 2+identityLen]) + + if identity != config.PreSharedKeyIdentity { + return nil, errors.New("tls: unexpected identity") + } + + if config.PreSharedKey == nil { + return nil, errors.New("tls: pre-shared key not configured") + } + + // Process the remainder of the ClientKeyExchange to compute the base + // pre-master secret. + newCkx := new(clientKeyExchangeMsg) + newCkx.ciphertext = ckx.ciphertext[2+identityLen:] + otherSecret, err := ka.base.processClientKeyExchange(config, cert, newCkx, version) + if err != nil { + return nil, err + } + + if otherSecret == nil { + // Special-case for the plain PSK key exchanges. + otherSecret = make([]byte, len(config.PreSharedKey)) + } + return makePSKPremaster(otherSecret, config.PreSharedKey), nil +} + +func (ka *pskKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + if len(skx.key) < 2 { + return errServerKeyExchange + } + identityLen := (int(skx.key[0]) << 8) | int(skx.key[1]) + if 2+identityLen > len(skx.key) { + return errServerKeyExchange + } + ka.identityHint = string(skx.key[2 : 2+identityLen]) + + // Process the remainder of the ServerKeyExchange. + newSkx := new(serverKeyExchangeMsg) + newSkx.key = skx.key[2+identityLen:] + return ka.base.processServerKeyExchange(config, clientHello, serverHello, cert, newSkx) +} + +func (ka *pskKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + // The server only sends an identity hint but, for purposes of + // test code, the server always sends the hint and it is + // required to match. + if ka.identityHint != config.PreSharedKeyIdentity { + return nil, nil, errors.New("tls: unexpected identity") + } + + // Serialize the identity. + bytes := make([]byte, 2+len(config.PreSharedKeyIdentity)) + bytes[0] = byte(len(config.PreSharedKeyIdentity) >> 8) + bytes[1] = byte(len(config.PreSharedKeyIdentity)) + copy(bytes[2:], []byte(config.PreSharedKeyIdentity)) + + // Append the base key exchange's ClientKeyExchange. + otherSecret, baseCkx, err := ka.base.generateClientKeyExchange(config, clientHello, cert) + if err != nil { + return nil, nil, err + } + ckx := new(clientKeyExchangeMsg) + ckx.ciphertext = append(bytes, baseCkx.ciphertext...) + + if config.PreSharedKey == nil { + return nil, nil, errors.New("tls: pre-shared key not configured") + } + if otherSecret == nil { + otherSecret = make([]byte, len(config.PreSharedKey)) + } + return makePSKPremaster(otherSecret, config.PreSharedKey), ckx, nil +} + +func (ka *pskKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm { + return 0 +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/packet_adapter.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/packet_adapter.go new file mode 100644 index 000000000..a8da311bb --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/packet_adapter.go @@ -0,0 +1,179 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "time" +) + +// opcodePacket signals a packet, encoded with a 32-bit length prefix, followed +// by the payload. +const opcodePacket = byte('P') + +// opcodeTimeout signals a read timeout, encoded by a 64-bit number of +// nanoseconds. On receipt, the peer should reply with +// opcodeTimeoutAck. opcodeTimeout may only be sent by the Go side. +const opcodeTimeout = byte('T') + +// opcodeTimeoutAck acknowledges a read timeout. This opcode has no payload and +// may only be sent by the C side. Timeout ACKs act as a synchronization point +// at the timeout, to bracket one flight of messages from C. +const opcodeTimeoutAck = byte('t') + +type packetAdaptor struct { + net.Conn + debug *recordingConn +} + +// newPacketAdaptor wraps a reliable streaming net.Conn into a reliable +// packet-based net.Conn. The stream contains packets and control commands, +// distinguished by a one byte opcode. +func newPacketAdaptor(conn net.Conn) *packetAdaptor { + return &packetAdaptor{conn, nil} +} + +func (p *packetAdaptor) log(message string, data []byte) { + if p.debug == nil { + return + } + + p.debug.LogSpecial(message, data) +} + +func (p *packetAdaptor) readOpcode() (byte, error) { + out := make([]byte, 1) + if _, err := io.ReadFull(p.Conn, out); err != nil { + return 0, err + } + return out[0], nil +} + +func (p *packetAdaptor) readPacketBody() ([]byte, error) { + var length uint32 + if err := binary.Read(p.Conn, binary.BigEndian, &length); err != nil { + return nil, err + } + out := make([]byte, length) + if _, err := io.ReadFull(p.Conn, out); err != nil { + return nil, err + } + return out, nil +} + +func (p *packetAdaptor) Read(b []byte) (int, error) { + opcode, err := p.readOpcode() + if err != nil { + return 0, err + } + if opcode != opcodePacket { + return 0, fmt.Errorf("unexpected opcode '%d'", opcode) + } + out, err := p.readPacketBody() + if err != nil { + return 0, err + } + return copy(b, out), nil +} + +func (p *packetAdaptor) Write(b []byte) (int, error) { + payload := make([]byte, 1+4+len(b)) + payload[0] = opcodePacket + binary.BigEndian.PutUint32(payload[1:5], uint32(len(b))) + copy(payload[5:], b) + if _, err := p.Conn.Write(payload); err != nil { + return 0, err + } + return len(b), nil +} + +// SendReadTimeout instructs the peer to simulate a read timeout. It then waits +// for acknowledgement of the timeout, buffering any packets received since +// then. The packets are then returned. +func (p *packetAdaptor) SendReadTimeout(d time.Duration) ([][]byte, error) { + p.log("Simulating read timeout: "+d.String(), nil) + + payload := make([]byte, 1+8) + payload[0] = opcodeTimeout + binary.BigEndian.PutUint64(payload[1:], uint64(d.Nanoseconds())) + if _, err := p.Conn.Write(payload); err != nil { + return nil, err + } + + var packets [][]byte + for { + opcode, err := p.readOpcode() + if err != nil { + return nil, err + } + switch opcode { + case opcodeTimeoutAck: + p.log("Received timeout ACK", nil) + // Done! Return the packets buffered and continue. + return packets, nil + case opcodePacket: + // Buffer the packet for the caller to process. + packet, err := p.readPacketBody() + if err != nil { + return nil, err + } + p.log("Simulating dropped packet", packet) + packets = append(packets, packet) + default: + return nil, fmt.Errorf("unexpected opcode '%d'", opcode) + } + } +} + +type replayAdaptor struct { + net.Conn + prevWrite []byte +} + +// newReplayAdaptor wraps a packeted net.Conn. It transforms it into +// one which, after writing a packet, always replays the previous +// write. +func newReplayAdaptor(conn net.Conn) net.Conn { + return &replayAdaptor{Conn: conn} +} + +func (r *replayAdaptor) Write(b []byte) (int, error) { + n, err := r.Conn.Write(b) + + // Replay the previous packet and save the current one to + // replay next. + if r.prevWrite != nil { + r.Conn.Write(r.prevWrite) + } + r.prevWrite = append(r.prevWrite[:0], b...) + + return n, err +} + +type damageAdaptor struct { + net.Conn + damage bool +} + +// newDamageAdaptor wraps a packeted net.Conn. It transforms it into one which +// optionally damages the final byte of every Write() call. +func newDamageAdaptor(conn net.Conn) *damageAdaptor { + return &damageAdaptor{Conn: conn} +} + +func (d *damageAdaptor) setDamage(damage bool) { + d.damage = damage +} + +func (d *damageAdaptor) Write(b []byte) (int, error) { + if d.damage && len(b) > 0 { + b = append([]byte{}, b...) + b[len(b)-1]++ + } + return d.Conn.Write(b) +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/poly1305.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/poly1305.go new file mode 100644 index 000000000..4a5f826f7 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/poly1305.go @@ -0,0 +1,32 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package poly1305 implements Poly1305 one-time message authentication code as specified in http://cr.yp.to/mac/poly1305-20050329.pdf. + +Poly1305 is a fast, one-time authentication function. It is infeasible for an +attacker to generate an authenticator for a message without the key. However, a +key must only be used for a single message. Authenticating two different +messages with the same key allows an attacker to forge authenticators for other +messages with the same key. + +Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was +used with a fixed key in order to generate one-time keys from an nonce. +However, in this package AES isn't used and the one-time key is specified +directly. +*/ +package poly1305 // import "golang.org/x/crypto/poly1305" + +import "crypto/subtle" + +// TagSize is the size, in bytes, of a poly1305 authenticator. +const TagSize = 16 + +// Verify returns true if mac is a valid authenticator for m with the given +// key. +func Verify(mac *[16]byte, m []byte, key *[32]byte) bool { + var tmp [16]byte + Sum(&tmp, m, key) + return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1 +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/poly1305_test.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/poly1305_test.go new file mode 100644 index 000000000..b3e92310b --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/poly1305_test.go @@ -0,0 +1,86 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package poly1305 + +import ( + "bytes" + "testing" + "unsafe" +) + +var testData = []struct { + in, k, correct []byte +}{ + { + []byte("Hello world!"), + []byte("this is 32-byte key for Poly1305"), + []byte{0xa6, 0xf7, 0x45, 0x00, 0x8f, 0x81, 0xc9, 0x16, 0xa2, 0x0d, 0xcc, 0x74, 0xee, 0xf2, 0xb2, 0xf0}, + }, + { + make([]byte, 32), + []byte("this is 32-byte key for Poly1305"), + []byte{0x49, 0xec, 0x78, 0x09, 0x0e, 0x48, 0x1e, 0xc6, 0xc2, 0x6b, 0x33, 0xb9, 0x1c, 0xcc, 0x03, 0x07}, + }, + { + make([]byte, 2007), + []byte("this is 32-byte key for Poly1305"), + []byte{0xda, 0x84, 0xbc, 0xab, 0x02, 0x67, 0x6c, 0x38, 0xcd, 0xb0, 0x15, 0x60, 0x42, 0x74, 0xc2, 0xaa}, + }, + { + make([]byte, 2007), + make([]byte, 32), + make([]byte, 16), + }, +} + +func testSum(t *testing.T, unaligned bool) { + var out [16]byte + var key [32]byte + + for i, v := range testData { + in := v.in + if unaligned { + in = unalignBytes(in) + } + copy(key[:], v.k) + Sum(&out, in, &key) + if !bytes.Equal(out[:], v.correct) { + t.Errorf("%d: expected %x, got %x", i, v.correct, out[:]) + } + } +} + +func TestSum(t *testing.T) { testSum(t, false) } +func TestSumUnaligned(t *testing.T) { testSum(t, true) } + +func benchmark(b *testing.B, size int, unaligned bool) { + var out [16]byte + var key [32]byte + in := make([]byte, size) + if unaligned { + in = unalignBytes(in) + } + b.SetBytes(int64(len(in))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + Sum(&out, in, &key) + } +} + +func Benchmark64(b *testing.B) { benchmark(b, 64, false) } +func Benchmark1K(b *testing.B) { benchmark(b, 1024, false) } +func Benchmark64Unaligned(b *testing.B) { benchmark(b, 64, true) } +func Benchmark1KUnaligned(b *testing.B) { benchmark(b, 1024, true) } + +func unalignBytes(in []byte) []byte { + out := make([]byte, len(in)+1) + if uintptr(unsafe.Pointer(&out[0]))&(unsafe.Alignof(uint32(0))-1) == 0 { + out = out[1:] + } else { + out = out[:len(in)] + } + copy(out, in) + return out +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_amd64.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_amd64.go new file mode 100644 index 000000000..6775c703f --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_amd64.go @@ -0,0 +1,24 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine + +package poly1305 + +// This function is implemented in poly1305_amd64.s + +//go:noescape + +func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + var mPtr *byte + if len(m) > 0 { + mPtr = &m[0] + } + poly1305(out, mPtr, uint64(len(m)), key) +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_arm.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_arm.go new file mode 100644 index 000000000..50b979c24 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_arm.go @@ -0,0 +1,24 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build arm,!gccgo,!appengine + +package poly1305 + +// This function is implemented in poly1305_arm.s + +//go:noescape + +func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + var mPtr *byte + if len(m) > 0 { + mPtr = &m[0] + } + poly1305_auth_armv6(out, mPtr, uint32(len(m)), key) +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_ref.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_ref.go new file mode 100644 index 000000000..0b24fc78b --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/poly1305/sum_ref.go @@ -0,0 +1,1531 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64,!arm gccgo appengine + +package poly1305 + +// Based on original, public domain implementation from NaCl by D. J. +// Bernstein. + +import "math" + +const ( + alpham80 = 0.00000000558793544769287109375 + alpham48 = 24.0 + alpham16 = 103079215104.0 + alpha0 = 6755399441055744.0 + alpha18 = 1770887431076116955136.0 + alpha32 = 29014219670751100192948224.0 + alpha50 = 7605903601369376408980219232256.0 + alpha64 = 124615124604835863084731911901282304.0 + alpha82 = 32667107224410092492483962313449748299776.0 + alpha96 = 535217884764734955396857238543560676143529984.0 + alpha112 = 35076039295941670036888435985190792471742381031424.0 + alpha130 = 9194973245195333150150082162901855101712434733101613056.0 + scale = 0.0000000000000000000000000000000000000036734198463196484624023016788195177431833298649127735047148490821200539357960224151611328125 + offset0 = 6755408030990331.0 + offset1 = 29014256564239239022116864.0 + offset2 = 124615283061160854719918951570079744.0 + offset3 = 535219245894202480694386063513315216128475136.0 +) + +// Sum generates an authenticator for m using a one-time key and puts the +// 16-byte result into out. Authenticating two different messages with the same +// key allows an attacker to forge messages at will. +func Sum(out *[16]byte, m []byte, key *[32]byte) { + r := key + s := key[16:] + var ( + y7 float64 + y6 float64 + y1 float64 + y0 float64 + y5 float64 + y4 float64 + x7 float64 + x6 float64 + x1 float64 + x0 float64 + y3 float64 + y2 float64 + x5 float64 + r3lowx0 float64 + x4 float64 + r0lowx6 float64 + x3 float64 + r3highx0 float64 + x2 float64 + r0highx6 float64 + r0lowx0 float64 + sr1lowx6 float64 + r0highx0 float64 + sr1highx6 float64 + sr3low float64 + r1lowx0 float64 + sr2lowx6 float64 + r1highx0 float64 + sr2highx6 float64 + r2lowx0 float64 + sr3lowx6 float64 + r2highx0 float64 + sr3highx6 float64 + r1highx4 float64 + r1lowx4 float64 + r0highx4 float64 + r0lowx4 float64 + sr3highx4 float64 + sr3lowx4 float64 + sr2highx4 float64 + sr2lowx4 float64 + r0lowx2 float64 + r0highx2 float64 + r1lowx2 float64 + r1highx2 float64 + r2lowx2 float64 + r2highx2 float64 + sr3lowx2 float64 + sr3highx2 float64 + z0 float64 + z1 float64 + z2 float64 + z3 float64 + m0 int64 + m1 int64 + m2 int64 + m3 int64 + m00 uint32 + m01 uint32 + m02 uint32 + m03 uint32 + m10 uint32 + m11 uint32 + m12 uint32 + m13 uint32 + m20 uint32 + m21 uint32 + m22 uint32 + m23 uint32 + m30 uint32 + m31 uint32 + m32 uint32 + m33 uint64 + lbelow2 int32 + lbelow3 int32 + lbelow4 int32 + lbelow5 int32 + lbelow6 int32 + lbelow7 int32 + lbelow8 int32 + lbelow9 int32 + lbelow10 int32 + lbelow11 int32 + lbelow12 int32 + lbelow13 int32 + lbelow14 int32 + lbelow15 int32 + s00 uint32 + s01 uint32 + s02 uint32 + s03 uint32 + s10 uint32 + s11 uint32 + s12 uint32 + s13 uint32 + s20 uint32 + s21 uint32 + s22 uint32 + s23 uint32 + s30 uint32 + s31 uint32 + s32 uint32 + s33 uint32 + bits32 uint64 + f uint64 + f0 uint64 + f1 uint64 + f2 uint64 + f3 uint64 + f4 uint64 + g uint64 + g0 uint64 + g1 uint64 + g2 uint64 + g3 uint64 + g4 uint64 + ) + + var p int32 + + l := int32(len(m)) + + r00 := uint32(r[0]) + + r01 := uint32(r[1]) + + r02 := uint32(r[2]) + r0 := int64(2151) + + r03 := uint32(r[3]) + r03 &= 15 + r0 <<= 51 + + r10 := uint32(r[4]) + r10 &= 252 + r01 <<= 8 + r0 += int64(r00) + + r11 := uint32(r[5]) + r02 <<= 16 + r0 += int64(r01) + + r12 := uint32(r[6]) + r03 <<= 24 + r0 += int64(r02) + + r13 := uint32(r[7]) + r13 &= 15 + r1 := int64(2215) + r0 += int64(r03) + + d0 := r0 + r1 <<= 51 + r2 := int64(2279) + + r20 := uint32(r[8]) + r20 &= 252 + r11 <<= 8 + r1 += int64(r10) + + r21 := uint32(r[9]) + r12 <<= 16 + r1 += int64(r11) + + r22 := uint32(r[10]) + r13 <<= 24 + r1 += int64(r12) + + r23 := uint32(r[11]) + r23 &= 15 + r2 <<= 51 + r1 += int64(r13) + + d1 := r1 + r21 <<= 8 + r2 += int64(r20) + + r30 := uint32(r[12]) + r30 &= 252 + r22 <<= 16 + r2 += int64(r21) + + r31 := uint32(r[13]) + r23 <<= 24 + r2 += int64(r22) + + r32 := uint32(r[14]) + r2 += int64(r23) + r3 := int64(2343) + + d2 := r2 + r3 <<= 51 + + r33 := uint32(r[15]) + r33 &= 15 + r31 <<= 8 + r3 += int64(r30) + + r32 <<= 16 + r3 += int64(r31) + + r33 <<= 24 + r3 += int64(r32) + + r3 += int64(r33) + h0 := alpha32 - alpha32 + + d3 := r3 + h1 := alpha32 - alpha32 + + h2 := alpha32 - alpha32 + + h3 := alpha32 - alpha32 + + h4 := alpha32 - alpha32 + + r0low := math.Float64frombits(uint64(d0)) + h5 := alpha32 - alpha32 + + r1low := math.Float64frombits(uint64(d1)) + h6 := alpha32 - alpha32 + + r2low := math.Float64frombits(uint64(d2)) + h7 := alpha32 - alpha32 + + r0low -= alpha0 + + r1low -= alpha32 + + r2low -= alpha64 + + r0high := r0low + alpha18 + + r3low := math.Float64frombits(uint64(d3)) + + r1high := r1low + alpha50 + sr1low := scale * r1low + + r2high := r2low + alpha82 + sr2low := scale * r2low + + r0high -= alpha18 + r0high_stack := r0high + + r3low -= alpha96 + + r1high -= alpha50 + r1high_stack := r1high + + sr1high := sr1low + alpham80 + + r0low -= r0high + + r2high -= alpha82 + sr3low = scale * r3low + + sr2high := sr2low + alpham48 + + r1low -= r1high + r1low_stack := r1low + + sr1high -= alpham80 + sr1high_stack := sr1high + + r2low -= r2high + r2low_stack := r2low + + sr2high -= alpham48 + sr2high_stack := sr2high + + r3high := r3low + alpha112 + r0low_stack := r0low + + sr1low -= sr1high + sr1low_stack := sr1low + + sr3high := sr3low + alpham16 + r2high_stack := r2high + + sr2low -= sr2high + sr2low_stack := sr2low + + r3high -= alpha112 + r3high_stack := r3high + + sr3high -= alpham16 + sr3high_stack := sr3high + + r3low -= r3high + r3low_stack := r3low + + sr3low -= sr3high + sr3low_stack := sr3low + + if l < 16 { + goto addatmost15bytes + } + + m00 = uint32(m[p+0]) + m0 = 2151 + + m0 <<= 51 + m1 = 2215 + m01 = uint32(m[p+1]) + + m1 <<= 51 + m2 = 2279 + m02 = uint32(m[p+2]) + + m2 <<= 51 + m3 = 2343 + m03 = uint32(m[p+3]) + + m10 = uint32(m[p+4]) + m01 <<= 8 + m0 += int64(m00) + + m11 = uint32(m[p+5]) + m02 <<= 16 + m0 += int64(m01) + + m12 = uint32(m[p+6]) + m03 <<= 24 + m0 += int64(m02) + + m13 = uint32(m[p+7]) + m3 <<= 51 + m0 += int64(m03) + + m20 = uint32(m[p+8]) + m11 <<= 8 + m1 += int64(m10) + + m21 = uint32(m[p+9]) + m12 <<= 16 + m1 += int64(m11) + + m22 = uint32(m[p+10]) + m13 <<= 24 + m1 += int64(m12) + + m23 = uint32(m[p+11]) + m1 += int64(m13) + + m30 = uint32(m[p+12]) + m21 <<= 8 + m2 += int64(m20) + + m31 = uint32(m[p+13]) + m22 <<= 16 + m2 += int64(m21) + + m32 = uint32(m[p+14]) + m23 <<= 24 + m2 += int64(m22) + + m33 = uint64(m[p+15]) + m2 += int64(m23) + + d0 = m0 + m31 <<= 8 + m3 += int64(m30) + + d1 = m1 + m32 <<= 16 + m3 += int64(m31) + + d2 = m2 + m33 += 256 + + m33 <<= 24 + m3 += int64(m32) + + m3 += int64(m33) + d3 = m3 + + p += 16 + l -= 16 + + z0 = math.Float64frombits(uint64(d0)) + + z1 = math.Float64frombits(uint64(d1)) + + z2 = math.Float64frombits(uint64(d2)) + + z3 = math.Float64frombits(uint64(d3)) + + z0 -= alpha0 + + z1 -= alpha32 + + z2 -= alpha64 + + z3 -= alpha96 + + h0 += z0 + + h1 += z1 + + h3 += z2 + + h5 += z3 + + if l < 16 { + goto multiplyaddatmost15bytes + } + +multiplyaddatleast16bytes: + + m2 = 2279 + m20 = uint32(m[p+8]) + y7 = h7 + alpha130 + + m2 <<= 51 + m3 = 2343 + m21 = uint32(m[p+9]) + y6 = h6 + alpha130 + + m3 <<= 51 + m0 = 2151 + m22 = uint32(m[p+10]) + y1 = h1 + alpha32 + + m0 <<= 51 + m1 = 2215 + m23 = uint32(m[p+11]) + y0 = h0 + alpha32 + + m1 <<= 51 + m30 = uint32(m[p+12]) + y7 -= alpha130 + + m21 <<= 8 + m2 += int64(m20) + m31 = uint32(m[p+13]) + y6 -= alpha130 + + m22 <<= 16 + m2 += int64(m21) + m32 = uint32(m[p+14]) + y1 -= alpha32 + + m23 <<= 24 + m2 += int64(m22) + m33 = uint64(m[p+15]) + y0 -= alpha32 + + m2 += int64(m23) + m00 = uint32(m[p+0]) + y5 = h5 + alpha96 + + m31 <<= 8 + m3 += int64(m30) + m01 = uint32(m[p+1]) + y4 = h4 + alpha96 + + m32 <<= 16 + m02 = uint32(m[p+2]) + x7 = h7 - y7 + y7 *= scale + + m33 += 256 + m03 = uint32(m[p+3]) + x6 = h6 - y6 + y6 *= scale + + m33 <<= 24 + m3 += int64(m31) + m10 = uint32(m[p+4]) + x1 = h1 - y1 + + m01 <<= 8 + m3 += int64(m32) + m11 = uint32(m[p+5]) + x0 = h0 - y0 + + m3 += int64(m33) + m0 += int64(m00) + m12 = uint32(m[p+6]) + y5 -= alpha96 + + m02 <<= 16 + m0 += int64(m01) + m13 = uint32(m[p+7]) + y4 -= alpha96 + + m03 <<= 24 + m0 += int64(m02) + d2 = m2 + x1 += y7 + + m0 += int64(m03) + d3 = m3 + x0 += y6 + + m11 <<= 8 + m1 += int64(m10) + d0 = m0 + x7 += y5 + + m12 <<= 16 + m1 += int64(m11) + x6 += y4 + + m13 <<= 24 + m1 += int64(m12) + y3 = h3 + alpha64 + + m1 += int64(m13) + d1 = m1 + y2 = h2 + alpha64 + + x0 += x1 + + x6 += x7 + + y3 -= alpha64 + r3low = r3low_stack + + y2 -= alpha64 + r0low = r0low_stack + + x5 = h5 - y5 + r3lowx0 = r3low * x0 + r3high = r3high_stack + + x4 = h4 - y4 + r0lowx6 = r0low * x6 + r0high = r0high_stack + + x3 = h3 - y3 + r3highx0 = r3high * x0 + sr1low = sr1low_stack + + x2 = h2 - y2 + r0highx6 = r0high * x6 + sr1high = sr1high_stack + + x5 += y3 + r0lowx0 = r0low * x0 + r1low = r1low_stack + + h6 = r3lowx0 + r0lowx6 + sr1lowx6 = sr1low * x6 + r1high = r1high_stack + + x4 += y2 + r0highx0 = r0high * x0 + sr2low = sr2low_stack + + h7 = r3highx0 + r0highx6 + sr1highx6 = sr1high * x6 + sr2high = sr2high_stack + + x3 += y1 + r1lowx0 = r1low * x0 + r2low = r2low_stack + + h0 = r0lowx0 + sr1lowx6 + sr2lowx6 = sr2low * x6 + r2high = r2high_stack + + x2 += y0 + r1highx0 = r1high * x0 + sr3low = sr3low_stack + + h1 = r0highx0 + sr1highx6 + sr2highx6 = sr2high * x6 + sr3high = sr3high_stack + + x4 += x5 + r2lowx0 = r2low * x0 + z2 = math.Float64frombits(uint64(d2)) + + h2 = r1lowx0 + sr2lowx6 + sr3lowx6 = sr3low * x6 + + x2 += x3 + r2highx0 = r2high * x0 + z3 = math.Float64frombits(uint64(d3)) + + h3 = r1highx0 + sr2highx6 + sr3highx6 = sr3high * x6 + + r1highx4 = r1high * x4 + z2 -= alpha64 + + h4 = r2lowx0 + sr3lowx6 + r1lowx4 = r1low * x4 + + r0highx4 = r0high * x4 + z3 -= alpha96 + + h5 = r2highx0 + sr3highx6 + r0lowx4 = r0low * x4 + + h7 += r1highx4 + sr3highx4 = sr3high * x4 + + h6 += r1lowx4 + sr3lowx4 = sr3low * x4 + + h5 += r0highx4 + sr2highx4 = sr2high * x4 + + h4 += r0lowx4 + sr2lowx4 = sr2low * x4 + + h3 += sr3highx4 + r0lowx2 = r0low * x2 + + h2 += sr3lowx4 + r0highx2 = r0high * x2 + + h1 += sr2highx4 + r1lowx2 = r1low * x2 + + h0 += sr2lowx4 + r1highx2 = r1high * x2 + + h2 += r0lowx2 + r2lowx2 = r2low * x2 + + h3 += r0highx2 + r2highx2 = r2high * x2 + + h4 += r1lowx2 + sr3lowx2 = sr3low * x2 + + h5 += r1highx2 + sr3highx2 = sr3high * x2 + + p += 16 + l -= 16 + h6 += r2lowx2 + + h7 += r2highx2 + + z1 = math.Float64frombits(uint64(d1)) + h0 += sr3lowx2 + + z0 = math.Float64frombits(uint64(d0)) + h1 += sr3highx2 + + z1 -= alpha32 + + z0 -= alpha0 + + h5 += z3 + + h3 += z2 + + h1 += z1 + + h0 += z0 + + if l >= 16 { + goto multiplyaddatleast16bytes + } + +multiplyaddatmost15bytes: + + y7 = h7 + alpha130 + + y6 = h6 + alpha130 + + y1 = h1 + alpha32 + + y0 = h0 + alpha32 + + y7 -= alpha130 + + y6 -= alpha130 + + y1 -= alpha32 + + y0 -= alpha32 + + y5 = h5 + alpha96 + + y4 = h4 + alpha96 + + x7 = h7 - y7 + y7 *= scale + + x6 = h6 - y6 + y6 *= scale + + x1 = h1 - y1 + + x0 = h0 - y0 + + y5 -= alpha96 + + y4 -= alpha96 + + x1 += y7 + + x0 += y6 + + x7 += y5 + + x6 += y4 + + y3 = h3 + alpha64 + + y2 = h2 + alpha64 + + x0 += x1 + + x6 += x7 + + y3 -= alpha64 + r3low = r3low_stack + + y2 -= alpha64 + r0low = r0low_stack + + x5 = h5 - y5 + r3lowx0 = r3low * x0 + r3high = r3high_stack + + x4 = h4 - y4 + r0lowx6 = r0low * x6 + r0high = r0high_stack + + x3 = h3 - y3 + r3highx0 = r3high * x0 + sr1low = sr1low_stack + + x2 = h2 - y2 + r0highx6 = r0high * x6 + sr1high = sr1high_stack + + x5 += y3 + r0lowx0 = r0low * x0 + r1low = r1low_stack + + h6 = r3lowx0 + r0lowx6 + sr1lowx6 = sr1low * x6 + r1high = r1high_stack + + x4 += y2 + r0highx0 = r0high * x0 + sr2low = sr2low_stack + + h7 = r3highx0 + r0highx6 + sr1highx6 = sr1high * x6 + sr2high = sr2high_stack + + x3 += y1 + r1lowx0 = r1low * x0 + r2low = r2low_stack + + h0 = r0lowx0 + sr1lowx6 + sr2lowx6 = sr2low * x6 + r2high = r2high_stack + + x2 += y0 + r1highx0 = r1high * x0 + sr3low = sr3low_stack + + h1 = r0highx0 + sr1highx6 + sr2highx6 = sr2high * x6 + sr3high = sr3high_stack + + x4 += x5 + r2lowx0 = r2low * x0 + + h2 = r1lowx0 + sr2lowx6 + sr3lowx6 = sr3low * x6 + + x2 += x3 + r2highx0 = r2high * x0 + + h3 = r1highx0 + sr2highx6 + sr3highx6 = sr3high * x6 + + r1highx4 = r1high * x4 + + h4 = r2lowx0 + sr3lowx6 + r1lowx4 = r1low * x4 + + r0highx4 = r0high * x4 + + h5 = r2highx0 + sr3highx6 + r0lowx4 = r0low * x4 + + h7 += r1highx4 + sr3highx4 = sr3high * x4 + + h6 += r1lowx4 + sr3lowx4 = sr3low * x4 + + h5 += r0highx4 + sr2highx4 = sr2high * x4 + + h4 += r0lowx4 + sr2lowx4 = sr2low * x4 + + h3 += sr3highx4 + r0lowx2 = r0low * x2 + + h2 += sr3lowx4 + r0highx2 = r0high * x2 + + h1 += sr2highx4 + r1lowx2 = r1low * x2 + + h0 += sr2lowx4 + r1highx2 = r1high * x2 + + h2 += r0lowx2 + r2lowx2 = r2low * x2 + + h3 += r0highx2 + r2highx2 = r2high * x2 + + h4 += r1lowx2 + sr3lowx2 = sr3low * x2 + + h5 += r1highx2 + sr3highx2 = sr3high * x2 + + h6 += r2lowx2 + + h7 += r2highx2 + + h0 += sr3lowx2 + + h1 += sr3highx2 + +addatmost15bytes: + + if l == 0 { + goto nomorebytes + } + + lbelow2 = l - 2 + + lbelow3 = l - 3 + + lbelow2 >>= 31 + lbelow4 = l - 4 + + m00 = uint32(m[p+0]) + lbelow3 >>= 31 + p += lbelow2 + + m01 = uint32(m[p+1]) + lbelow4 >>= 31 + p += lbelow3 + + m02 = uint32(m[p+2]) + p += lbelow4 + m0 = 2151 + + m03 = uint32(m[p+3]) + m0 <<= 51 + m1 = 2215 + + m0 += int64(m00) + m01 &^= uint32(lbelow2) + + m02 &^= uint32(lbelow3) + m01 -= uint32(lbelow2) + + m01 <<= 8 + m03 &^= uint32(lbelow4) + + m0 += int64(m01) + lbelow2 -= lbelow3 + + m02 += uint32(lbelow2) + lbelow3 -= lbelow4 + + m02 <<= 16 + m03 += uint32(lbelow3) + + m03 <<= 24 + m0 += int64(m02) + + m0 += int64(m03) + lbelow5 = l - 5 + + lbelow6 = l - 6 + lbelow7 = l - 7 + + lbelow5 >>= 31 + lbelow8 = l - 8 + + lbelow6 >>= 31 + p += lbelow5 + + m10 = uint32(m[p+4]) + lbelow7 >>= 31 + p += lbelow6 + + m11 = uint32(m[p+5]) + lbelow8 >>= 31 + p += lbelow7 + + m12 = uint32(m[p+6]) + m1 <<= 51 + p += lbelow8 + + m13 = uint32(m[p+7]) + m10 &^= uint32(lbelow5) + lbelow4 -= lbelow5 + + m10 += uint32(lbelow4) + lbelow5 -= lbelow6 + + m11 &^= uint32(lbelow6) + m11 += uint32(lbelow5) + + m11 <<= 8 + m1 += int64(m10) + + m1 += int64(m11) + m12 &^= uint32(lbelow7) + + lbelow6 -= lbelow7 + m13 &^= uint32(lbelow8) + + m12 += uint32(lbelow6) + lbelow7 -= lbelow8 + + m12 <<= 16 + m13 += uint32(lbelow7) + + m13 <<= 24 + m1 += int64(m12) + + m1 += int64(m13) + m2 = 2279 + + lbelow9 = l - 9 + m3 = 2343 + + lbelow10 = l - 10 + lbelow11 = l - 11 + + lbelow9 >>= 31 + lbelow12 = l - 12 + + lbelow10 >>= 31 + p += lbelow9 + + m20 = uint32(m[p+8]) + lbelow11 >>= 31 + p += lbelow10 + + m21 = uint32(m[p+9]) + lbelow12 >>= 31 + p += lbelow11 + + m22 = uint32(m[p+10]) + m2 <<= 51 + p += lbelow12 + + m23 = uint32(m[p+11]) + m20 &^= uint32(lbelow9) + lbelow8 -= lbelow9 + + m20 += uint32(lbelow8) + lbelow9 -= lbelow10 + + m21 &^= uint32(lbelow10) + m21 += uint32(lbelow9) + + m21 <<= 8 + m2 += int64(m20) + + m2 += int64(m21) + m22 &^= uint32(lbelow11) + + lbelow10 -= lbelow11 + m23 &^= uint32(lbelow12) + + m22 += uint32(lbelow10) + lbelow11 -= lbelow12 + + m22 <<= 16 + m23 += uint32(lbelow11) + + m23 <<= 24 + m2 += int64(m22) + + m3 <<= 51 + lbelow13 = l - 13 + + lbelow13 >>= 31 + lbelow14 = l - 14 + + lbelow14 >>= 31 + p += lbelow13 + lbelow15 = l - 15 + + m30 = uint32(m[p+12]) + lbelow15 >>= 31 + p += lbelow14 + + m31 = uint32(m[p+13]) + p += lbelow15 + m2 += int64(m23) + + m32 = uint32(m[p+14]) + m30 &^= uint32(lbelow13) + lbelow12 -= lbelow13 + + m30 += uint32(lbelow12) + lbelow13 -= lbelow14 + + m3 += int64(m30) + m31 &^= uint32(lbelow14) + + m31 += uint32(lbelow13) + m32 &^= uint32(lbelow15) + + m31 <<= 8 + lbelow14 -= lbelow15 + + m3 += int64(m31) + m32 += uint32(lbelow14) + d0 = m0 + + m32 <<= 16 + m33 = uint64(lbelow15 + 1) + d1 = m1 + + m33 <<= 24 + m3 += int64(m32) + d2 = m2 + + m3 += int64(m33) + d3 = m3 + + z3 = math.Float64frombits(uint64(d3)) + + z2 = math.Float64frombits(uint64(d2)) + + z1 = math.Float64frombits(uint64(d1)) + + z0 = math.Float64frombits(uint64(d0)) + + z3 -= alpha96 + + z2 -= alpha64 + + z1 -= alpha32 + + z0 -= alpha0 + + h5 += z3 + + h3 += z2 + + h1 += z1 + + h0 += z0 + + y7 = h7 + alpha130 + + y6 = h6 + alpha130 + + y1 = h1 + alpha32 + + y0 = h0 + alpha32 + + y7 -= alpha130 + + y6 -= alpha130 + + y1 -= alpha32 + + y0 -= alpha32 + + y5 = h5 + alpha96 + + y4 = h4 + alpha96 + + x7 = h7 - y7 + y7 *= scale + + x6 = h6 - y6 + y6 *= scale + + x1 = h1 - y1 + + x0 = h0 - y0 + + y5 -= alpha96 + + y4 -= alpha96 + + x1 += y7 + + x0 += y6 + + x7 += y5 + + x6 += y4 + + y3 = h3 + alpha64 + + y2 = h2 + alpha64 + + x0 += x1 + + x6 += x7 + + y3 -= alpha64 + r3low = r3low_stack + + y2 -= alpha64 + r0low = r0low_stack + + x5 = h5 - y5 + r3lowx0 = r3low * x0 + r3high = r3high_stack + + x4 = h4 - y4 + r0lowx6 = r0low * x6 + r0high = r0high_stack + + x3 = h3 - y3 + r3highx0 = r3high * x0 + sr1low = sr1low_stack + + x2 = h2 - y2 + r0highx6 = r0high * x6 + sr1high = sr1high_stack + + x5 += y3 + r0lowx0 = r0low * x0 + r1low = r1low_stack + + h6 = r3lowx0 + r0lowx6 + sr1lowx6 = sr1low * x6 + r1high = r1high_stack + + x4 += y2 + r0highx0 = r0high * x0 + sr2low = sr2low_stack + + h7 = r3highx0 + r0highx6 + sr1highx6 = sr1high * x6 + sr2high = sr2high_stack + + x3 += y1 + r1lowx0 = r1low * x0 + r2low = r2low_stack + + h0 = r0lowx0 + sr1lowx6 + sr2lowx6 = sr2low * x6 + r2high = r2high_stack + + x2 += y0 + r1highx0 = r1high * x0 + sr3low = sr3low_stack + + h1 = r0highx0 + sr1highx6 + sr2highx6 = sr2high * x6 + sr3high = sr3high_stack + + x4 += x5 + r2lowx0 = r2low * x0 + + h2 = r1lowx0 + sr2lowx6 + sr3lowx6 = sr3low * x6 + + x2 += x3 + r2highx0 = r2high * x0 + + h3 = r1highx0 + sr2highx6 + sr3highx6 = sr3high * x6 + + r1highx4 = r1high * x4 + + h4 = r2lowx0 + sr3lowx6 + r1lowx4 = r1low * x4 + + r0highx4 = r0high * x4 + + h5 = r2highx0 + sr3highx6 + r0lowx4 = r0low * x4 + + h7 += r1highx4 + sr3highx4 = sr3high * x4 + + h6 += r1lowx4 + sr3lowx4 = sr3low * x4 + + h5 += r0highx4 + sr2highx4 = sr2high * x4 + + h4 += r0lowx4 + sr2lowx4 = sr2low * x4 + + h3 += sr3highx4 + r0lowx2 = r0low * x2 + + h2 += sr3lowx4 + r0highx2 = r0high * x2 + + h1 += sr2highx4 + r1lowx2 = r1low * x2 + + h0 += sr2lowx4 + r1highx2 = r1high * x2 + + h2 += r0lowx2 + r2lowx2 = r2low * x2 + + h3 += r0highx2 + r2highx2 = r2high * x2 + + h4 += r1lowx2 + sr3lowx2 = sr3low * x2 + + h5 += r1highx2 + sr3highx2 = sr3high * x2 + + h6 += r2lowx2 + + h7 += r2highx2 + + h0 += sr3lowx2 + + h1 += sr3highx2 + +nomorebytes: + + y7 = h7 + alpha130 + + y0 = h0 + alpha32 + + y1 = h1 + alpha32 + + y2 = h2 + alpha64 + + y7 -= alpha130 + + y3 = h3 + alpha64 + + y4 = h4 + alpha96 + + y5 = h5 + alpha96 + + x7 = h7 - y7 + y7 *= scale + + y0 -= alpha32 + + y1 -= alpha32 + + y2 -= alpha64 + + h6 += x7 + + y3 -= alpha64 + + y4 -= alpha96 + + y5 -= alpha96 + + y6 = h6 + alpha130 + + x0 = h0 - y0 + + x1 = h1 - y1 + + x2 = h2 - y2 + + y6 -= alpha130 + + x0 += y7 + + x3 = h3 - y3 + + x4 = h4 - y4 + + x5 = h5 - y5 + + x6 = h6 - y6 + + y6 *= scale + + x2 += y0 + + x3 += y1 + + x4 += y2 + + x0 += y6 + + x5 += y3 + + x6 += y4 + + x2 += x3 + + x0 += x1 + + x4 += x5 + + x6 += y5 + + x2 += offset1 + d1 = int64(math.Float64bits(x2)) + + x0 += offset0 + d0 = int64(math.Float64bits(x0)) + + x4 += offset2 + d2 = int64(math.Float64bits(x4)) + + x6 += offset3 + d3 = int64(math.Float64bits(x6)) + + f0 = uint64(d0) + + f1 = uint64(d1) + bits32 = math.MaxUint64 + + f2 = uint64(d2) + bits32 >>= 32 + + f3 = uint64(d3) + f = f0 >> 32 + + f0 &= bits32 + f &= 255 + + f1 += f + g0 = f0 + 5 + + g = g0 >> 32 + g0 &= bits32 + + f = f1 >> 32 + f1 &= bits32 + + f &= 255 + g1 = f1 + g + + g = g1 >> 32 + f2 += f + + f = f2 >> 32 + g1 &= bits32 + + f2 &= bits32 + f &= 255 + + f3 += f + g2 = f2 + g + + g = g2 >> 32 + g2 &= bits32 + + f4 = f3 >> 32 + f3 &= bits32 + + f4 &= 255 + g3 = f3 + g + + g = g3 >> 32 + g3 &= bits32 + + g4 = f4 + g + + g4 = g4 - 4 + s00 = uint32(s[0]) + + f = uint64(int64(g4) >> 63) + s01 = uint32(s[1]) + + f0 &= f + g0 &^= f + s02 = uint32(s[2]) + + f1 &= f + f0 |= g0 + s03 = uint32(s[3]) + + g1 &^= f + f2 &= f + s10 = uint32(s[4]) + + f3 &= f + g2 &^= f + s11 = uint32(s[5]) + + g3 &^= f + f1 |= g1 + s12 = uint32(s[6]) + + f2 |= g2 + f3 |= g3 + s13 = uint32(s[7]) + + s01 <<= 8 + f0 += uint64(s00) + s20 = uint32(s[8]) + + s02 <<= 16 + f0 += uint64(s01) + s21 = uint32(s[9]) + + s03 <<= 24 + f0 += uint64(s02) + s22 = uint32(s[10]) + + s11 <<= 8 + f1 += uint64(s10) + s23 = uint32(s[11]) + + s12 <<= 16 + f1 += uint64(s11) + s30 = uint32(s[12]) + + s13 <<= 24 + f1 += uint64(s12) + s31 = uint32(s[13]) + + f0 += uint64(s03) + f1 += uint64(s13) + s32 = uint32(s[14]) + + s21 <<= 8 + f2 += uint64(s20) + s33 = uint32(s[15]) + + s22 <<= 16 + f2 += uint64(s21) + + s23 <<= 24 + f2 += uint64(s22) + + s31 <<= 8 + f3 += uint64(s30) + + s32 <<= 16 + f3 += uint64(s31) + + s33 <<= 24 + f3 += uint64(s32) + + f2 += uint64(s23) + f3 += uint64(s33) + + out[0] = byte(f0) + f0 >>= 8 + out[1] = byte(f0) + f0 >>= 8 + out[2] = byte(f0) + f0 >>= 8 + out[3] = byte(f0) + f0 >>= 8 + f1 += f0 + + out[4] = byte(f1) + f1 >>= 8 + out[5] = byte(f1) + f1 >>= 8 + out[6] = byte(f1) + f1 >>= 8 + out[7] = byte(f1) + f1 >>= 8 + f2 += f1 + + out[8] = byte(f2) + f2 >>= 8 + out[9] = byte(f2) + f2 >>= 8 + out[10] = byte(f2) + f2 >>= 8 + out[11] = byte(f2) + f2 >>= 8 + f3 += f2 + + out[12] = byte(f3) + f3 >>= 8 + out[13] = byte(f3) + f3 >>= 8 + out[14] = byte(f3) + f3 >>= 8 + out[15] = byte(f3) +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/prf.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/prf.go new file mode 100644 index 000000000..cfc383dc5 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/prf.go @@ -0,0 +1,478 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "crypto" + "crypto/hmac" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "hash" +) + +// Split a premaster secret in two as specified in RFC 4346, section 5. +func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { + s1 = secret[0 : (len(secret)+1)/2] + s2 = secret[len(secret)/2:] + return +} + +// pHash implements the P_hash function, as defined in RFC 4346, section 5. +func pHash(result, secret, seed []byte, hash func() hash.Hash) { + h := hmac.New(hash, secret) + h.Write(seed) + a := h.Sum(nil) + + j := 0 + for j < len(result) { + h.Reset() + h.Write(a) + h.Write(seed) + b := h.Sum(nil) + todo := len(b) + if j+todo > len(result) { + todo = len(result) - j + } + copy(result[j:j+todo], b) + j += todo + + h.Reset() + h.Write(a) + a = h.Sum(nil) + } +} + +// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5. +func prf10(result, secret, label, seed []byte) { + hashSHA1 := sha1.New + hashMD5 := md5.New + + labelAndSeed := make([]byte, len(label)+len(seed)) + copy(labelAndSeed, label) + copy(labelAndSeed[len(label):], seed) + + s1, s2 := splitPreMasterSecret(secret) + pHash(result, s1, labelAndSeed, hashMD5) + result2 := make([]byte, len(result)) + pHash(result2, s2, labelAndSeed, hashSHA1) + + for i, b := range result2 { + result[i] ^= b + } +} + +// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, section 5. +func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) { + return func(result, secret, label, seed []byte) { + labelAndSeed := make([]byte, len(label)+len(seed)) + copy(labelAndSeed, label) + copy(labelAndSeed[len(label):], seed) + + pHash(result, secret, labelAndSeed, hashFunc) + } +} + +// prf30 implements the SSL 3.0 pseudo-random function, as defined in +// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6. +func prf30(result, secret, label, seed []byte) { + hashSHA1 := sha1.New() + hashMD5 := md5.New() + + done := 0 + i := 0 + // RFC5246 section 6.3 says that the largest PRF output needed is 128 + // bytes. Since no more ciphersuites will be added to SSLv3, this will + // remain true. Each iteration gives us 16 bytes so 10 iterations will + // be sufficient. + var b [11]byte + for done < len(result) { + for j := 0; j <= i; j++ { + b[j] = 'A' + byte(i) + } + + hashSHA1.Reset() + hashSHA1.Write(b[:i+1]) + hashSHA1.Write(secret) + hashSHA1.Write(seed) + digest := hashSHA1.Sum(nil) + + hashMD5.Reset() + hashMD5.Write(secret) + hashMD5.Write(digest) + + done += copy(result[done:], hashMD5.Sum(nil)) + i++ + } +} + +const ( + tlsRandomLength = 32 // Length of a random nonce in TLS 1.1. + masterSecretLength = 48 // Length of a master secret in TLS 1.1. + finishedVerifyLength = 12 // Length of verify_data in a Finished message. +) + +var masterSecretLabel = []byte("master secret") +var extendedMasterSecretLabel = []byte("extended master secret") +var keyExpansionLabel = []byte("key expansion") +var clientFinishedLabel = []byte("client finished") +var serverFinishedLabel = []byte("server finished") +var finishedLabel = []byte("finished") +var channelIDLabel = []byte("TLS Channel ID signature\x00") +var channelIDResumeLabel = []byte("Resumption\x00") + +func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) { + switch version { + case VersionSSL30: + return prf30 + case VersionTLS10, VersionTLS11: + return prf10 + case VersionTLS12: + return prf12(suite.hash().New) + } + panic("unknown version") +} + +// masterFromPreMasterSecret generates the master secret from the pre-master +// secret. See http://tools.ietf.org/html/rfc5246#section-8.1 +func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte { + var seed [tlsRandomLength * 2]byte + copy(seed[0:len(clientRandom)], clientRandom) + copy(seed[len(clientRandom):], serverRandom) + masterSecret := make([]byte, masterSecretLength) + prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) + return masterSecret +} + +// extendedMasterFromPreMasterSecret generates the master secret from the +// pre-master secret when the Triple Handshake fix is in effect. See +// https://tools.ietf.org/html/rfc7627 +func extendedMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret []byte, h finishedHash) []byte { + masterSecret := make([]byte, masterSecretLength) + prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, h.Sum()) + return masterSecret +} + +// keysFromMasterSecret generates the connection keys from the master +// secret, given the lengths of the MAC key, cipher key and IV, as defined in +// RFC 2246, section 6.3. +func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { + var seed [tlsRandomLength * 2]byte + copy(seed[0:len(clientRandom)], serverRandom) + copy(seed[len(serverRandom):], clientRandom) + + n := 2*macLen + 2*keyLen + 2*ivLen + keyMaterial := make([]byte, n) + prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) + clientMAC = keyMaterial[:macLen] + keyMaterial = keyMaterial[macLen:] + serverMAC = keyMaterial[:macLen] + keyMaterial = keyMaterial[macLen:] + clientKey = keyMaterial[:keyLen] + keyMaterial = keyMaterial[keyLen:] + serverKey = keyMaterial[:keyLen] + keyMaterial = keyMaterial[keyLen:] + clientIV = keyMaterial[:ivLen] + keyMaterial = keyMaterial[ivLen:] + serverIV = keyMaterial[:ivLen] + return +} + +func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash { + var ret finishedHash + + if version >= VersionTLS12 { + ret.hash = cipherSuite.hash() + + ret.client = ret.hash.New() + ret.server = ret.hash.New() + + if version == VersionTLS12 { + ret.prf = prf12(ret.hash.New) + } else { + ret.secret = make([]byte, ret.hash.Size()) + } + } else { + ret.hash = crypto.MD5SHA1 + + ret.client = sha1.New() + ret.server = sha1.New() + ret.clientMD5 = md5.New() + ret.serverMD5 = md5.New() + + ret.prf = prf10 + } + + ret.buffer = []byte{} + ret.version = version + return ret +} + +// A finishedHash calculates the hash of a set of handshake messages suitable +// for including in a Finished message. +type finishedHash struct { + hash crypto.Hash + + client hash.Hash + server hash.Hash + + // Prior to TLS 1.2, an additional MD5 hash is required. + clientMD5 hash.Hash + serverMD5 hash.Hash + + // In TLS 1.2 (and SSL 3 for implementation convenience), a + // full buffer is required. + buffer []byte + + version uint16 + prf func(result, secret, label, seed []byte) + + // secret, in TLS 1.3, is the running input secret. + secret []byte +} + +func (h *finishedHash) Write(msg []byte) (n int, err error) { + h.client.Write(msg) + h.server.Write(msg) + + if h.version < VersionTLS12 { + h.clientMD5.Write(msg) + h.serverMD5.Write(msg) + } + + if h.buffer != nil { + h.buffer = append(h.buffer, msg...) + } + + return len(msg), nil +} + +func (h finishedHash) Sum() []byte { + if h.version >= VersionTLS12 { + return h.client.Sum(nil) + } + + out := make([]byte, 0, md5.Size+sha1.Size) + out = h.clientMD5.Sum(out) + return h.client.Sum(out) +} + +// finishedSum30 calculates the contents of the verify_data member of a SSLv3 +// Finished message given the MD5 and SHA1 hashes of a set of handshake +// messages. +func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic []byte) []byte { + md5.Write(magic) + md5.Write(masterSecret) + md5.Write(ssl30Pad1[:]) + md5Digest := md5.Sum(nil) + + md5.Reset() + md5.Write(masterSecret) + md5.Write(ssl30Pad2[:]) + md5.Write(md5Digest) + md5Digest = md5.Sum(nil) + + sha1.Write(magic) + sha1.Write(masterSecret) + sha1.Write(ssl30Pad1[:40]) + sha1Digest := sha1.Sum(nil) + + sha1.Reset() + sha1.Write(masterSecret) + sha1.Write(ssl30Pad2[:40]) + sha1.Write(sha1Digest) + sha1Digest = sha1.Sum(nil) + + ret := make([]byte, len(md5Digest)+len(sha1Digest)) + copy(ret, md5Digest) + copy(ret[len(md5Digest):], sha1Digest) + return ret +} + +var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54} +var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52} + +// clientSum returns the contents of the verify_data member of a client's +// Finished message. +func (h finishedHash) clientSum(baseKey []byte) []byte { + if h.version == VersionSSL30 { + return finishedSum30(h.clientMD5, h.client, baseKey, ssl3ClientFinishedMagic[:]) + } + + if h.version < VersionTLS13 { + out := make([]byte, finishedVerifyLength) + h.prf(out, baseKey, clientFinishedLabel, h.Sum()) + return out + } + + clientFinishedKey := hkdfExpandLabel(h.hash, baseKey, finishedLabel, nil, h.hash.Size()) + finishedHMAC := hmac.New(h.hash.New, clientFinishedKey) + finishedHMAC.Write(h.appendContextHashes(nil)) + return finishedHMAC.Sum(nil) +} + +// serverSum returns the contents of the verify_data member of a server's +// Finished message. +func (h finishedHash) serverSum(baseKey []byte) []byte { + if h.version == VersionSSL30 { + return finishedSum30(h.serverMD5, h.server, baseKey, ssl3ServerFinishedMagic[:]) + } + + if h.version < VersionTLS13 { + out := make([]byte, finishedVerifyLength) + h.prf(out, baseKey, serverFinishedLabel, h.Sum()) + return out + } + + serverFinishedKey := hkdfExpandLabel(h.hash, baseKey, finishedLabel, nil, h.hash.Size()) + finishedHMAC := hmac.New(h.hash.New, serverFinishedKey) + finishedHMAC.Write(h.appendContextHashes(nil)) + return finishedHMAC.Sum(nil) +} + +// hashForClientCertificateSSL3 returns the hash to be signed for client +// certificates in SSL 3.0. +func (h finishedHash) hashForClientCertificateSSL3(masterSecret []byte) []byte { + md5Hash := md5.New() + md5Hash.Write(h.buffer) + sha1Hash := sha1.New() + sha1Hash.Write(h.buffer) + return finishedSum30(md5Hash, sha1Hash, masterSecret, nil) +} + +// hashForChannelID returns the hash to be signed for TLS Channel +// ID. If a resumption, resumeHash has the previous handshake +// hash. Otherwise, it is nil. +func (h finishedHash) hashForChannelID(resumeHash []byte) []byte { + hash := sha256.New() + hash.Write(channelIDLabel) + if resumeHash != nil { + hash.Write(channelIDResumeLabel) + hash.Write(resumeHash) + } + hash.Write(h.Sum()) + return hash.Sum(nil) +} + +// discardHandshakeBuffer is called when there is no more need to +// buffer the entirety of the handshake messages. +func (h *finishedHash) discardHandshakeBuffer() { + h.buffer = nil +} + +// zeroSecretTLS13 returns the default all zeros secret for TLS 1.3, used when a +// given secret is not available in the handshake. See draft-ietf-tls-tls13-16, +// section 7.1. +func (h *finishedHash) zeroSecret() []byte { + return make([]byte, h.hash.Size()) +} + +// addEntropy incorporates ikm into the running TLS 1.3 secret with HKDF-Expand. +func (h *finishedHash) addEntropy(ikm []byte) { + h.secret = hkdfExtract(h.hash.New, h.secret, ikm) +} + +// hkdfExpandLabel implements TLS 1.3's HKDF-Expand-Label function, as defined +// in section 7.1 of draft-ietf-tls-tls13-16. +func hkdfExpandLabel(hash crypto.Hash, secret, label, hashValue []byte, length int) []byte { + if len(label) > 255 || len(hashValue) > 255 { + panic("hkdfExpandLabel: label or hashValue too long") + } + hkdfLabel := make([]byte, 3+9+len(label)+1+len(hashValue)) + x := hkdfLabel + x[0] = byte(length >> 8) + x[1] = byte(length) + x[2] = byte(9 + len(label)) + x = x[3:] + copy(x, []byte("TLS 1.3, ")) + x = x[9:] + copy(x, label) + x = x[len(label):] + x[0] = byte(len(hashValue)) + copy(x[1:], hashValue) + return hkdfExpand(hash.New, secret, hkdfLabel, length) +} + +// appendContextHashes returns the concatenation of the handshake hash and the +// resumption context hash, as used in TLS 1.3. +func (h *finishedHash) appendContextHashes(b []byte) []byte { + b = h.client.Sum(b) + return b +} + +// The following are labels for traffic secret derivation in TLS 1.3. +var ( + externalPSKBinderLabel = []byte("external psk binder key") + resumptionPSKBinderLabel = []byte("resumption psk binder key") + earlyTrafficLabel = []byte("client early traffic secret") + clientHandshakeTrafficLabel = []byte("client handshake traffic secret") + serverHandshakeTrafficLabel = []byte("server handshake traffic secret") + clientApplicationTrafficLabel = []byte("client application traffic secret") + serverApplicationTrafficLabel = []byte("server application traffic secret") + applicationTrafficLabel = []byte("application traffic secret") + exporterLabel = []byte("exporter master secret") + resumptionLabel = []byte("resumption master secret") +) + +// deriveSecret implements TLS 1.3's Derive-Secret function, as defined in +// section 7.1 of draft ietf-tls-tls13-16. +func (h *finishedHash) deriveSecret(label []byte) []byte { + return hkdfExpandLabel(h.hash, h.secret, label, h.appendContextHashes(nil), h.hash.Size()) +} + +// The following are context strings for CertificateVerify in TLS 1.3. +var ( + clientCertificateVerifyContextTLS13 = []byte("TLS 1.3, client CertificateVerify") + serverCertificateVerifyContextTLS13 = []byte("TLS 1.3, server CertificateVerify") + channelIDContextTLS13 = []byte("TLS 1.3, Channel ID") +) + +// certificateVerifyMessage returns the input to be signed for CertificateVerify +// in TLS 1.3. +func (h *finishedHash) certificateVerifyInput(context []byte) []byte { + const paddingLen = 64 + b := make([]byte, paddingLen, paddingLen+len(context)+1+2*h.hash.Size()) + for i := 0; i < paddingLen; i++ { + b[i] = 32 + } + b = append(b, context...) + b = append(b, 0) + b = h.appendContextHashes(b) + return b +} + +type trafficDirection int + +const ( + clientWrite trafficDirection = iota + serverWrite +) + +var ( + keyTLS13 = []byte("key") + ivTLS13 = []byte("iv") +) + +// deriveTrafficAEAD derives traffic keys and constructs an AEAD given a traffic +// secret. +func deriveTrafficAEAD(version uint16, suite *cipherSuite, secret []byte, side trafficDirection) interface{} { + key := hkdfExpandLabel(suite.hash(), secret, keyTLS13, nil, suite.keyLen) + iv := hkdfExpandLabel(suite.hash(), secret, ivTLS13, nil, suite.ivLen(version)) + + return suite.aead(version, key, iv) +} + +func updateTrafficSecret(hash crypto.Hash, secret []byte) []byte { + return hkdfExpandLabel(hash, secret, applicationTrafficLabel, nil, hash.Size()) +} + +func computePSKBinder(psk, label []byte, cipherSuite *cipherSuite, transcript, truncatedHello []byte) []byte { + finishedHash := newFinishedHash(VersionTLS13, cipherSuite) + finishedHash.addEntropy(psk) + binderKey := finishedHash.deriveSecret(label) + finishedHash.Write(transcript) + finishedHash.Write(truncatedHello) + return finishedHash.clientSum(binderKey) +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/recordingconn.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/recordingconn.go new file mode 100644 index 000000000..4dae43575 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/recordingconn.go @@ -0,0 +1,167 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "bufio" + "encoding/hex" + "errors" + "fmt" + "io" + "net" + "strconv" + "strings" + "sync" +) + +type flowType int + +const ( + readFlow flowType = iota + writeFlow + specialFlow +) + +type flow struct { + flowType flowType + message string + data []byte +} + +// recordingConn is a net.Conn that records the traffic that passes through it. +// WriteTo can be used to produce output that can be later be loaded with +// ParseTestData. +type recordingConn struct { + net.Conn + sync.Mutex + flows []flow + isDatagram bool + local, peer string +} + +func (r *recordingConn) appendFlow(flowType flowType, message string, data []byte) { + r.Lock() + defer r.Unlock() + + if l := len(r.flows); flowType == specialFlow || r.isDatagram || l == 0 || r.flows[l-1].flowType != flowType { + buf := make([]byte, len(data)) + copy(buf, data) + r.flows = append(r.flows, flow{flowType, message, buf}) + } else { + r.flows[l-1].data = append(r.flows[l-1].data, data...) + } +} + +func (r *recordingConn) Read(b []byte) (n int, err error) { + if n, err = r.Conn.Read(b); n == 0 { + return + } + r.appendFlow(readFlow, "", b[:n]) + return +} + +func (r *recordingConn) Write(b []byte) (n int, err error) { + if n, err = r.Conn.Write(b); n == 0 { + return + } + r.appendFlow(writeFlow, "", b[:n]) + return +} + +// LogSpecial appends an entry to the record of type 'special'. +func (r *recordingConn) LogSpecial(message string, data []byte) { + r.appendFlow(specialFlow, message, data) +} + +// WriteTo writes hex dumps to w that contains the recorded traffic. +func (r *recordingConn) WriteTo(w io.Writer) { + fmt.Fprintf(w, ">>> runner is %s, shim is %s\n", r.local, r.peer) + for i, flow := range r.flows { + switch flow.flowType { + case readFlow: + fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, r.peer, r.local) + case writeFlow: + fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, r.local, r.peer) + case specialFlow: + fmt.Fprintf(w, ">>> Flow %d %q\n", i+1, flow.message) + } + + if flow.data != nil { + dumper := hex.Dumper(w) + dumper.Write(flow.data) + dumper.Close() + } + } +} + +func (r *recordingConn) Transcript() []byte { + var ret []byte + for _, flow := range r.flows { + if flow.flowType != writeFlow { + continue + } + ret = append(ret, flow.data...) + } + return ret +} + +func parseTestData(r io.Reader) (flows [][]byte, err error) { + var currentFlow []byte + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := scanner.Text() + // If the line starts with ">>> " then it marks the beginning + // of a new flow. + if strings.HasPrefix(line, ">>> ") { + if len(currentFlow) > 0 || len(flows) > 0 { + flows = append(flows, currentFlow) + currentFlow = nil + } + continue + } + + // Otherwise the line is a line of hex dump that looks like: + // 00000170 fc f5 06 bf (...) |.....X{&?......!| + // (Some bytes have been omitted from the middle section.) + + if i := strings.IndexByte(line, ' '); i >= 0 { + line = line[i:] + } else { + return nil, errors.New("invalid test data") + } + + if i := strings.IndexByte(line, '|'); i >= 0 { + line = line[:i] + } else { + return nil, errors.New("invalid test data") + } + + hexBytes := strings.Fields(line) + for _, hexByte := range hexBytes { + val, err := strconv.ParseUint(hexByte, 16, 8) + if err != nil { + return nil, errors.New("invalid hex byte in test data: " + err.Error()) + } + currentFlow = append(currentFlow, byte(val)) + } + } + + if len(currentFlow) > 0 { + flows = append(flows, currentFlow) + } + + return flows, nil +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_1024_cert.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_1024_cert.pem new file mode 100644 index 000000000..4de4f49a3 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_1024_cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICWDCCAcGgAwIBAgIJAPuwTC6rEJsMMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTQwNDIzMjA1MDQwWhcNMTcwNDIyMjA1MDQwWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB +gQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92kWdGMdAQhLci +HnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiFKKAnHmUcrgfV +W28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQABo1AwTjAdBgNV +HQ4EFgQUi3XVrMsIvg4fZbf6Vr5sp3Xaha8wHwYDVR0jBBgwFoAUi3XVrMsIvg4f +Zbf6Vr5sp3Xaha8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQA76Hht +ldY9avcTGSwbwoiuIqv0jTL1fHFnzy3RHMLDh+Lpvolc5DSrSJHCP5WuK0eeJXhr +T5oQpHL9z/cCDLAKCKRa4uV0fhEdOWBqyR9p8y5jJtye72t6CuFUV5iqcpF4BH4f +j2VNHwsSrJwkD4QUGlUtH7vwnQmyCFxZMmWAJg== +-----END CERTIFICATE----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_1024_key.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_1024_key.pem new file mode 100644 index 000000000..2a3b9dedc --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_1024_key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANgryKYy5GL/TfPQ +rVmLRae98Ue/CVh7Ir01rpclhpSggMC0H3aRZ0Yx0BCEtyIecCORcsjpbXk6hXeA +D8SVFnXFSnFMyGM/o/JjnCpPmvrLwXFuKIUooCceZRyuB9Vbby1D7SuQsYyvJG2u +6Rc6BcG/uByuZTsbWMLZrtaqZ4jxAgMBAAECgYEAgHLT0V3gM6r8iOnwd4q4IwpM +epNbXEYeyEtDqPBVXa9Zkif1oiCYOy+SMJ6Lqyxm+duNVzDNKgHKGM3xkJ/+LXkc +QJYMUWHqC3Q/nPQycKHDNma9q1X8VcJPSP5aYY0H+CR6EqMZcstyNNvp1FhUlIJm +FW44stxtYhXXSCCAlAECQQDxLywZ7h7PLJmbh72v3mDqzjeQ+q2Pmt7BOxTG37af +h5Wh0P5lSUJQtZU0AUuRhFMEIBKVKub1eGNCmZYAclSRAkEA5XNB0VRp7Au104mg +8K2lihjXN3bZ5p7xNASakY5HXUvqRvEtCyRoyXL8M6c5prza2oAZN2oMRmBI2YJ4 +oqSeYQJAC+mdjwZQ5UC5sZHpz5b3SIG5AuMu0Wn/2KF3bD8+gPCsdlqhRhVxPhVJ +8lCiD+TuSMTgxhdhYvx4QqDdZNZA0QJBAOTXThaL3VSZ3U/MXSKN3aNc4RElTXAQ +p7pcuRhg0dZAB7mXgngxaP053EVcDEi65H+18PBuqS1rjFy7Hrv/+SECQQC+9Fcs +dNprpUXNNqKI7xJoWwdXeVDJc60ysGkHmN2ahlaCMe8HZb0KSfuwOqw8H5Ta3JfS +OgN1ATK6IwQINjyh +-----END PRIVATE KEY----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_chain_cert.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_chain_cert.pem new file mode 100644 index 000000000..8eb6e60ed --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_chain_cert.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIC0jCCAbqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBD +QTAeFw0xNjAyMjgyMDI3MDNaFw0yNjAyMjUyMDI3MDNaMBgxFjAUBgNVBAMMDUNs +aWVudCBDZXJ0IEEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRvaz8 +CC/cshpCafJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/ +kLRcH89M/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3 +tHb+xs2PSs8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+c +IDs2rQ+lP7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1 +z7C8jU50Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9V +iLeXANgZi+Xx9KgfAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYI +KwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBFEVbmYl+2RtNw +rDftRDF1v2QUbcN2ouSnQDHxeDQdSgasLzT3ui8iYu0Rw2WWcZ0DV5e0ztGPhWq7 +AO0B120aFRMOY+4+bzu9Q2FFkQqc7/fKTvTDzIJI5wrMnFvUfzzvxh3OHWMYSs/w +giq33hTKeHEq6Jyk3btCny0Ycecyc3yGXH10sizUfiHlhviCkDuESk8mFDwDDzqW +ZF0IipzFbEDHoIxLlm3GQxpiLoEV4k8KYJp3R5KBLFyxM6UGPz8h72mIPCJp2RuK +MYgF91UDvVzvnYm6TfseM2+ewKirC00GOrZ7rEcFvtxnKSqYf4ckqfNdSU1Y+RRC +1ngWZ7Ih +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICwjCCAaqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJQyBS +b290IENBMB4XDTE2MDIyODIwMjcwM1oXDTI2MDIyNTIwMjcwM1owDzENMAsGA1UE +AwwEQiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALsSCYmDip2D +GkjFxw7ykz26JSjELkl6ArlYjFJ3aT/SCh8qbS4gln7RH8CPBd78oFdfhIKQrwtZ +3/q21ykD9BAS3qHe2YdcJfm8/kWAy5DvXk6NXU4qX334KofBAEpgdA/igEFq1P1l +HAuIfZCpMRfT+i5WohVsGi8f/NgpRvVaMONLNfgw57mz1lbtFeBEISmX0kbsuJxF +Qj/Bwhi5/0HAEXG8e7zN4cEx0yPRvmOATRdVb/8dW2pwOHRJq9R5M0NUkIsTSnL7 +6N/z8hRAHMsV3IudC5Yd7GXW1AGu9a+iKU+Q4xcZCoj0DC99tL4VKujrV1kAeqsM +cz5/dKzi6+cCAwEAAaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AQYwDQYJKoZIhvcNAQELBQADggEBAIIeZiEeNhWWQ8Y4D+AGDwqUUeG8NjCbKrXQ +BlHg5wZ8xftFaiP1Dp/UAezmx2LNazdmuwrYB8lm3FVTyaPDTKEGIPS4wJKHgqH1 +QPDhqNm85ey7TEtI9oYjsNim/Rb+iGkIAMXaxt58SzxbjvP0kMr1JfJIZbic9vye +NwIspMFIpP3FB8ywyu0T0hWtCQgL4J47nigCHpOu58deP88fS/Nyz/fyGVWOZ76b +WhWwgM3P3X95fQ3d7oFPR/bVh0YV+Cf861INwplokXgXQ3/TCQ+HNXeAMWn3JLWv +XFwk8owk9dq/kQGdndGgy3KTEW4ctPX5GNhf3LJ9Q7dLji4ReQ4= +-----END CERTIFICATE----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_chain_key.pem b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_chain_key.pem new file mode 100644 index 000000000..d94d70442 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/rsa_chain_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRvaz8CC/cshpC +afJo4jLkHEoBqDLhdgFelJoAiQUyIqyWl2O7YHPnpJH+TgR7oelzNzt/kLRcH89M +/TszB6zqyLTC4aqmvzKL0peD/jL2LWBucR0WXIvjA3zoRuF/x86+rYH3tHb+xs2P +Ss8EGL/Ev+ss+qTzTGEn26fuGNHkNw6tOwPpc+o8+wUtzf/kAthamo+cIDs2rQ+l +P7+aLZTLeU/q4gcLutlzcK5imex5xy2jPkweq48kijK0kIzl1cPlA5d1z7C8jU50 +Pj9X9sQDJTN32j7UYRisJeeYQF8GaaN8SbrDI6zHgKzrRLyxDt/KQa9ViLeXANgZ +i+Xx9KgfAgMBAAECggEBAK0VjSJzkyPaamcyTVSWjo7GdaBGcK60lk657RjR+lK0 +YJ7pkej4oM2hdsVZFsP8Cs4E33nXLa/0pDsRov/qrp0WQm2skwqGMC1I/bZ0WRPk +wHaDrBBfESWnJDX/AGpVtlyOjPmgmK6J2usMPihQUDkKdAYrVWJePrMIxt1q6BMe +iczs3qriMmtY3bUc4UyUwJ5fhDLjshHvfuIpYQyI6EXZM6dZksn9LylXJnigY6QJ +HxOYO0BDwOsZ8yQ8J8afLk88i0GizEkgE1z3REtQUwgWfxr1WV/ud+T6/ZhSAgH9 +042mQvSFZnIUSEsmCvjhWuAunfxHKCTcAoYISWfzWpkCgYEA7gpf3HHU5Tn+CgUn +1X5uGpG3DmcMgfeGgs2r2f/IIg/5Ac1dfYILiybL1tN9zbyLCJfcbFpWBc9hJL6f +CPc5hUiwWFJqBJewxQkC1Ae/HakHbip+IZ+Jr0842O4BAArvixk4Lb7/N2Ct9sTE +NJO6RtK9lbEZ5uK61DglHy8CS2UCgYEA4ZC1o36kPAMQBggajgnucb2yuUEelk0f +AEr+GI32MGE+93xMr7rAhBoqLg4AITyIfEnOSQ5HwagnIHonBbv1LV/Gf9ursx8Z +YOGbvT8zzzC+SU1bkDzdjAYnFQVGIjMtKOBJ3K07++ypwX1fr4QsQ8uKL8WSOWwt +Z3Bym6XiZzMCgYADnhy+2OwHX85AkLt+PyGlPbmuelpyTzS4IDAQbBa6jcuW/2wA +UE2km75VUXmD+u2R/9zVuLm99NzhFhSMqlUxdV1YukfqMfP5yp1EY6m/5aW7QuIP +2MDa7TVL9rIFMiVZ09RKvbBbQxjhuzPQKL6X/PPspnhiTefQ+dl2k9xREQKBgHDS +fMfGNEeAEKezrfSVqxphE9/tXms3L+ZpnCaT+yu/uEr5dTIAawKoQ6i9f/sf1/Sy +xedsqR+IB+oKrzIDDWMgoJybN4pkZ8E5lzhVQIjFjKgFdWLzzqyW9z1gYfABQPlN +FiS20WX0vgP1vcKAjdNrHzc9zyHBpgQzDmAj3NZZAoGBAI8vKCKdH7w3aL5CNkZQ +2buIeWNA2HZazVwAGG5F2TU/LmXfRKnG6dX5bkU+AkBZh56jNZy//hfFSewJB4Kk +buB7ERSdaNbO21zXt9FEA3+z0RfMd/Zv2vlIWOSB5nzl/7UKti3sribK6s9ZVLfi +SxpiPQ8d/hmSGwn4ksrWUsJD +-----END PRIVATE KEY----- diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/runner.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/runner.go new file mode 100644 index 000000000..d90485c8f --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/runner.go @@ -0,0 +1,10468 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/hex" + "encoding/json" + "encoding/pem" + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "math/big" + "net" + "os" + "os/exec" + "path" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + "syscall" + "time" +) + +var ( + useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind") + useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb") + useLLDB = flag.Bool("lldb", false, "If true, run BoringSSL code under lldb") + flagDebug = flag.Bool("debug", false, "Hexdump the contents of the connection") + mallocTest = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.") + mallocTestDebug = flag.Bool("malloc-test-debug", false, "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.") + jsonOutput = flag.String("json-output", "", "The file to output JSON results to.") + pipe = flag.Bool("pipe", false, "If true, print status output suitable for piping into another program.") + testToRun = flag.String("test", "", "The pattern to filter tests to run, or empty to run all tests") + numWorkers = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.") + shimPath = flag.String("shim-path", "../../../build/ssl/test/bssl_shim", "The location of the shim binary.") + resourceDir = flag.String("resource-dir", ".", "The directory in which to find certificate and key files.") + fuzzer = flag.Bool("fuzzer", false, "If true, tests against a BoringSSL built in fuzzer mode.") + transcriptDir = flag.String("transcript-dir", "", "The directory in which to write transcripts.") + idleTimeout = flag.Duration("idle-timeout", 15*time.Second, "The number of seconds to wait for a read or write to bssl_shim.") + deterministic = flag.Bool("deterministic", false, "If true, uses a deterministic PRNG in the runner.") + allowUnimplemented = flag.Bool("allow-unimplemented", false, "If true, report pass even if some tests are unimplemented.") + looseErrors = flag.Bool("loose-errors", false, "If true, allow shims to report an untranslated error code.") + shimConfigFile = flag.String("shim-config", "", "A config file to use to configure the tests for this shim.") + includeDisabled = flag.Bool("include-disabled", false, "If true, also runs disabled tests.") + repeatUntilFailure = flag.Bool("repeat-until-failure", false, "If true, the first selected test will be run repeatedly until failure.") +) + +// ShimConfigurations is used with the “json” package and represents a shim +// config file. +type ShimConfiguration struct { + // DisabledTests maps from a glob-based pattern to a freeform string. + // The glob pattern is used to exclude tests from being run and the + // freeform string is unparsed but expected to explain why the test is + // disabled. + DisabledTests map[string]string + + // ErrorMap maps from expected error strings to the correct error + // string for the shim in question. For example, it might map + // “:NO_SHARED_CIPHER:” (a BoringSSL error string) to something + // like “SSL_ERROR_NO_CYPHER_OVERLAP”. + ErrorMap map[string]string +} + +var shimConfig ShimConfiguration + +type testCert int + +const ( + testCertRSA testCert = iota + testCertRSA1024 + testCertRSAChain + testCertECDSAP224 + testCertECDSAP256 + testCertECDSAP384 + testCertECDSAP521 +) + +const ( + rsaCertificateFile = "cert.pem" + rsa1024CertificateFile = "rsa_1024_cert.pem" + rsaChainCertificateFile = "rsa_chain_cert.pem" + ecdsaP224CertificateFile = "ecdsa_p224_cert.pem" + ecdsaP256CertificateFile = "ecdsa_p256_cert.pem" + ecdsaP384CertificateFile = "ecdsa_p384_cert.pem" + ecdsaP521CertificateFile = "ecdsa_p521_cert.pem" +) + +const ( + rsaKeyFile = "key.pem" + rsa1024KeyFile = "rsa_1024_key.pem" + rsaChainKeyFile = "rsa_chain_key.pem" + ecdsaP224KeyFile = "ecdsa_p224_key.pem" + ecdsaP256KeyFile = "ecdsa_p256_key.pem" + ecdsaP384KeyFile = "ecdsa_p384_key.pem" + ecdsaP521KeyFile = "ecdsa_p521_key.pem" + channelIDKeyFile = "channel_id_key.pem" +) + +var ( + rsaCertificate Certificate + rsa1024Certificate Certificate + rsaChainCertificate Certificate + ecdsaP224Certificate Certificate + ecdsaP256Certificate Certificate + ecdsaP384Certificate Certificate + ecdsaP521Certificate Certificate +) + +var testCerts = []struct { + id testCert + certFile, keyFile string + cert *Certificate +}{ + { + id: testCertRSA, + certFile: rsaCertificateFile, + keyFile: rsaKeyFile, + cert: &rsaCertificate, + }, + { + id: testCertRSA1024, + certFile: rsa1024CertificateFile, + keyFile: rsa1024KeyFile, + cert: &rsa1024Certificate, + }, + { + id: testCertRSAChain, + certFile: rsaChainCertificateFile, + keyFile: rsaChainKeyFile, + cert: &rsaChainCertificate, + }, + { + id: testCertECDSAP224, + certFile: ecdsaP224CertificateFile, + keyFile: ecdsaP224KeyFile, + cert: &ecdsaP224Certificate, + }, + { + id: testCertECDSAP256, + certFile: ecdsaP256CertificateFile, + keyFile: ecdsaP256KeyFile, + cert: &ecdsaP256Certificate, + }, + { + id: testCertECDSAP384, + certFile: ecdsaP384CertificateFile, + keyFile: ecdsaP384KeyFile, + cert: &ecdsaP384Certificate, + }, + { + id: testCertECDSAP521, + certFile: ecdsaP521CertificateFile, + keyFile: ecdsaP521KeyFile, + cert: &ecdsaP521Certificate, + }, +} + +var channelIDKey *ecdsa.PrivateKey +var channelIDBytes []byte + +var testOCSPResponse = []byte{1, 2, 3, 4} +var testSCTList = []byte{0, 6, 0, 4, 5, 6, 7, 8} + +var testOCSPExtension = append([]byte{byte(extensionStatusRequest) >> 8, byte(extensionStatusRequest), 0, 8, statusTypeOCSP, 0, 0, 4}, testOCSPResponse...) +var testSCTExtension = append([]byte{byte(extensionSignedCertificateTimestamp) >> 8, byte(extensionSignedCertificateTimestamp), 0, byte(len(testSCTList))}, testSCTList...) + +func initCertificates() { + for i := range testCerts { + cert, err := LoadX509KeyPair(path.Join(*resourceDir, testCerts[i].certFile), path.Join(*resourceDir, testCerts[i].keyFile)) + if err != nil { + panic(err) + } + cert.OCSPStaple = testOCSPResponse + cert.SignedCertificateTimestampList = testSCTList + *testCerts[i].cert = cert + } + + channelIDPEMBlock, err := ioutil.ReadFile(path.Join(*resourceDir, channelIDKeyFile)) + if err != nil { + panic(err) + } + channelIDDERBlock, _ := pem.Decode(channelIDPEMBlock) + if channelIDDERBlock.Type != "EC PRIVATE KEY" { + panic("bad key type") + } + channelIDKey, err = x509.ParseECPrivateKey(channelIDDERBlock.Bytes) + if err != nil { + panic(err) + } + if channelIDKey.Curve != elliptic.P256() { + panic("bad curve") + } + + channelIDBytes = make([]byte, 64) + writeIntPadded(channelIDBytes[:32], channelIDKey.X) + writeIntPadded(channelIDBytes[32:], channelIDKey.Y) +} + +func getRunnerCertificate(t testCert) Certificate { + for _, cert := range testCerts { + if cert.id == t { + return *cert.cert + } + } + panic("Unknown test certificate") +} + +func getShimCertificate(t testCert) string { + for _, cert := range testCerts { + if cert.id == t { + return cert.certFile + } + } + panic("Unknown test certificate") +} + +func getShimKey(t testCert) string { + for _, cert := range testCerts { + if cert.id == t { + return cert.keyFile + } + } + panic("Unknown test certificate") +} + +// encodeDERValues encodes a series of bytestrings in comma-separated-hex form. +func encodeDERValues(values [][]byte) string { + var ret string + for i, v := range values { + if i > 0 { + ret += "," + } + ret += hex.EncodeToString(v) + } + + return ret +} + +type testType int + +const ( + clientTest testType = iota + serverTest +) + +type protocol int + +const ( + tls protocol = iota + dtls +) + +const ( + alpn = 1 + npn = 2 +) + +type testCase struct { + testType testType + protocol protocol + name string + config Config + shouldFail bool + expectedError string + // expectedLocalError, if not empty, contains a substring that must be + // found in the local error. + expectedLocalError string + // expectedVersion, if non-zero, specifies the TLS version that must be + // negotiated. + expectedVersion uint16 + // expectedResumeVersion, if non-zero, specifies the TLS version that + // must be negotiated on resumption. If zero, expectedVersion is used. + expectedResumeVersion uint16 + // expectedCipher, if non-zero, specifies the TLS cipher suite that + // should be negotiated. + expectedCipher uint16 + // expectChannelID controls whether the connection should have + // negotiated a Channel ID with channelIDKey. + expectChannelID bool + // expectedNextProto controls whether the connection should + // negotiate a next protocol via NPN or ALPN. + expectedNextProto string + // expectNoNextProto, if true, means that no next protocol should be + // negotiated. + expectNoNextProto bool + // expectedNextProtoType, if non-zero, is the expected next + // protocol negotiation mechanism. + expectedNextProtoType int + // expectedSRTPProtectionProfile is the DTLS-SRTP profile that + // should be negotiated. If zero, none should be negotiated. + expectedSRTPProtectionProfile uint16 + // expectedOCSPResponse, if not nil, is the expected OCSP response to be received. + expectedOCSPResponse []uint8 + // expectedSCTList, if not nil, is the expected SCT list to be received. + expectedSCTList []uint8 + // expectedPeerSignatureAlgorithm, if not zero, is the signature + // algorithm that the peer should have used in the handshake. + expectedPeerSignatureAlgorithm signatureAlgorithm + // expectedCurveID, if not zero, is the curve that the handshake should + // have used. + expectedCurveID CurveID + // messageLen is the length, in bytes, of the test message that will be + // sent. + messageLen int + // messageCount is the number of test messages that will be sent. + messageCount int + // certFile is the path to the certificate to use for the server. + certFile string + // keyFile is the path to the private key to use for the server. + keyFile string + // resumeSession controls whether a second connection should be tested + // which attempts to resume the first session. + resumeSession bool + // resumeRenewedSession controls whether a third connection should be + // tested which attempts to resume the second connection's session. + resumeRenewedSession bool + // expectResumeRejected, if true, specifies that the attempted + // resumption must be rejected by the client. This is only valid for a + // serverTest. + expectResumeRejected bool + // resumeConfig, if not nil, points to a Config to be used on + // resumption. Unless newSessionsOnResume is set, + // SessionTicketKey, ServerSessionCache, and + // ClientSessionCache are copied from the initial connection's + // config. If nil, the initial connection's config is used. + resumeConfig *Config + // newSessionsOnResume, if true, will cause resumeConfig to + // use a different session resumption context. + newSessionsOnResume bool + // noSessionCache, if true, will cause the server to run without a + // session cache. + noSessionCache bool + // sendPrefix sends a prefix on the socket before actually performing a + // handshake. + sendPrefix string + // shimWritesFirst controls whether the shim sends an initial "hello" + // message before doing a roundtrip with the runner. + shimWritesFirst bool + // shimShutsDown, if true, runs a test where the shim shuts down the + // connection immediately after the handshake rather than echoing + // messages from the runner. + shimShutsDown bool + // renegotiate indicates the number of times the connection should be + // renegotiated during the exchange. + renegotiate int + // sendHalfHelloRequest, if true, causes the server to send half a + // HelloRequest when the handshake completes. + sendHalfHelloRequest bool + // renegotiateCiphers is a list of ciphersuite ids that will be + // switched in just before renegotiation. + renegotiateCiphers []uint16 + // replayWrites, if true, configures the underlying transport + // to replay every write it makes in DTLS tests. + replayWrites bool + // damageFirstWrite, if true, configures the underlying transport to + // damage the final byte of the first application data write. + damageFirstWrite bool + // exportKeyingMaterial, if non-zero, configures the test to exchange + // keying material and verify they match. + exportKeyingMaterial int + exportLabel string + exportContext string + useExportContext bool + // flags, if not empty, contains a list of command-line flags that will + // be passed to the shim program. + flags []string + // testTLSUnique, if true, causes the shim to send the tls-unique value + // which will be compared against the expected value. + testTLSUnique bool + // sendEmptyRecords is the number of consecutive empty records to send + // before and after the test message. + sendEmptyRecords int + // sendWarningAlerts is the number of consecutive warning alerts to send + // before and after the test message. + sendWarningAlerts int + // sendKeyUpdates is the number of consecutive key updates to send + // before and after the test message. + sendKeyUpdates int + // keyUpdateRequest is the KeyUpdateRequest value to send in KeyUpdate messages. + keyUpdateRequest byte + // expectMessageDropped, if true, means the test message is expected to + // be dropped by the client rather than echoed back. + expectMessageDropped bool + // expectPeerCertificate, if not nil, is the certificate chain the peer + // is expected to send. + expectPeerCertificate *Certificate +} + +var testCases []testCase + +func writeTranscript(test *testCase, num int, data []byte) { + if len(data) == 0 { + return + } + + protocol := "tls" + if test.protocol == dtls { + protocol = "dtls" + } + + side := "client" + if test.testType == serverTest { + side = "server" + } + + dir := path.Join(*transcriptDir, protocol, side) + if err := os.MkdirAll(dir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error making %s: %s\n", dir, err) + return + } + + name := fmt.Sprintf("%s-%d", test.name, num) + if err := ioutil.WriteFile(path.Join(dir, name), data, 0644); err != nil { + fmt.Fprintf(os.Stderr, "Error writing %s: %s\n", name, err) + } +} + +// A timeoutConn implements an idle timeout on each Read and Write operation. +type timeoutConn struct { + net.Conn + timeout time.Duration +} + +func (t *timeoutConn) Read(b []byte) (int, error) { + if err := t.SetReadDeadline(time.Now().Add(t.timeout)); err != nil { + return 0, err + } + return t.Conn.Read(b) +} + +func (t *timeoutConn) Write(b []byte) (int, error) { + if err := t.SetWriteDeadline(time.Now().Add(t.timeout)); err != nil { + return 0, err + } + return t.Conn.Write(b) +} + +func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, num int) error { + if !test.noSessionCache { + if config.ClientSessionCache == nil { + config.ClientSessionCache = NewLRUClientSessionCache(1) + } + if config.ServerSessionCache == nil { + config.ServerSessionCache = NewLRUServerSessionCache(1) + } + } + if test.testType == clientTest { + if len(config.Certificates) == 0 { + config.Certificates = []Certificate{rsaCertificate} + } + } else { + // Supply a ServerName to ensure a constant session cache key, + // rather than falling back to net.Conn.RemoteAddr. + if len(config.ServerName) == 0 { + config.ServerName = "test" + } + } + if *fuzzer { + config.Bugs.NullAllCiphers = true + } + if *deterministic { + config.Time = func() time.Time { return time.Unix(1234, 1234) } + } + + conn = &timeoutConn{conn, *idleTimeout} + + if test.protocol == dtls { + config.Bugs.PacketAdaptor = newPacketAdaptor(conn) + conn = config.Bugs.PacketAdaptor + } + + if *flagDebug || len(*transcriptDir) != 0 { + local, peer := "client", "server" + if test.testType == clientTest { + local, peer = peer, local + } + connDebug := &recordingConn{ + Conn: conn, + isDatagram: test.protocol == dtls, + local: local, + peer: peer, + } + conn = connDebug + if *flagDebug { + defer connDebug.WriteTo(os.Stdout) + } + if len(*transcriptDir) != 0 { + defer func() { + writeTranscript(test, num, connDebug.Transcript()) + }() + } + + if config.Bugs.PacketAdaptor != nil { + config.Bugs.PacketAdaptor.debug = connDebug + } + } + + if test.replayWrites { + conn = newReplayAdaptor(conn) + } + + var connDamage *damageAdaptor + if test.damageFirstWrite { + connDamage = newDamageAdaptor(conn) + conn = connDamage + } + + if test.sendPrefix != "" { + if _, err := conn.Write([]byte(test.sendPrefix)); err != nil { + return err + } + } + + var tlsConn *Conn + if test.testType == clientTest { + if test.protocol == dtls { + tlsConn = DTLSServer(conn, config) + } else { + tlsConn = Server(conn, config) + } + } else { + config.InsecureSkipVerify = true + if test.protocol == dtls { + tlsConn = DTLSClient(conn, config) + } else { + tlsConn = Client(conn, config) + } + } + defer tlsConn.Close() + + if err := tlsConn.Handshake(); err != nil { + return err + } + + // TODO(davidben): move all per-connection expectations into a dedicated + // expectations struct that can be specified separately for the two + // legs. + expectedVersion := test.expectedVersion + if isResume && test.expectedResumeVersion != 0 { + expectedVersion = test.expectedResumeVersion + } + connState := tlsConn.ConnectionState() + if vers := connState.Version; expectedVersion != 0 && vers != expectedVersion { + return fmt.Errorf("got version %x, expected %x", vers, expectedVersion) + } + + if cipher := connState.CipherSuite; test.expectedCipher != 0 && cipher != test.expectedCipher { + return fmt.Errorf("got cipher %x, expected %x", cipher, test.expectedCipher) + } + if didResume := connState.DidResume; isResume && didResume == test.expectResumeRejected { + return fmt.Errorf("didResume is %t, but we expected the opposite", didResume) + } + + if test.expectChannelID { + channelID := connState.ChannelID + if channelID == nil { + return fmt.Errorf("no channel ID negotiated") + } + if channelID.Curve != channelIDKey.Curve || + channelIDKey.X.Cmp(channelIDKey.X) != 0 || + channelIDKey.Y.Cmp(channelIDKey.Y) != 0 { + return fmt.Errorf("incorrect channel ID") + } + } + + if expected := test.expectedNextProto; expected != "" { + if actual := connState.NegotiatedProtocol; actual != expected { + return fmt.Errorf("next proto mismatch: got %s, wanted %s", actual, expected) + } + } + + if test.expectNoNextProto { + if actual := connState.NegotiatedProtocol; actual != "" { + return fmt.Errorf("got unexpected next proto %s", actual) + } + } + + if test.expectedNextProtoType != 0 { + if (test.expectedNextProtoType == alpn) != connState.NegotiatedProtocolFromALPN { + return fmt.Errorf("next proto type mismatch") + } + } + + if p := connState.SRTPProtectionProfile; p != test.expectedSRTPProtectionProfile { + return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile) + } + + if test.expectedOCSPResponse != nil && !bytes.Equal(test.expectedOCSPResponse, tlsConn.OCSPResponse()) { + return fmt.Errorf("OCSP Response mismatch: got %x, wanted %x", tlsConn.OCSPResponse(), test.expectedOCSPResponse) + } + + if test.expectedSCTList != nil && !bytes.Equal(test.expectedSCTList, connState.SCTList) { + return fmt.Errorf("SCT list mismatch") + } + + if expected := test.expectedPeerSignatureAlgorithm; expected != 0 && expected != connState.PeerSignatureAlgorithm { + return fmt.Errorf("expected peer to use signature algorithm %04x, but got %04x", expected, connState.PeerSignatureAlgorithm) + } + + if expected := test.expectedCurveID; expected != 0 && expected != connState.CurveID { + return fmt.Errorf("expected peer to use curve %04x, but got %04x", expected, connState.CurveID) + } + + if test.expectPeerCertificate != nil { + if len(connState.PeerCertificates) != len(test.expectPeerCertificate.Certificate) { + return fmt.Errorf("expected peer to send %d certificates, but got %d", len(connState.PeerCertificates), len(test.expectPeerCertificate.Certificate)) + } + for i, cert := range connState.PeerCertificates { + if !bytes.Equal(cert.Raw, test.expectPeerCertificate.Certificate[i]) { + return fmt.Errorf("peer certificate %d did not match", i+1) + } + } + } + + if test.exportKeyingMaterial > 0 { + actual := make([]byte, test.exportKeyingMaterial) + if _, err := io.ReadFull(tlsConn, actual); err != nil { + return err + } + expected, err := tlsConn.ExportKeyingMaterial(test.exportKeyingMaterial, []byte(test.exportLabel), []byte(test.exportContext), test.useExportContext) + if err != nil { + return err + } + if !bytes.Equal(actual, expected) { + return fmt.Errorf("keying material mismatch") + } + } + + if test.testTLSUnique { + var peersValue [12]byte + if _, err := io.ReadFull(tlsConn, peersValue[:]); err != nil { + return err + } + expected := tlsConn.ConnectionState().TLSUnique + if !bytes.Equal(peersValue[:], expected) { + return fmt.Errorf("tls-unique mismatch: peer sent %x, but %x was expected", peersValue[:], expected) + } + } + + if test.shimWritesFirst { + var buf [5]byte + _, err := io.ReadFull(tlsConn, buf[:]) + if err != nil { + return err + } + if string(buf[:]) != "hello" { + return fmt.Errorf("bad initial message") + } + } + + for i := 0; i < test.sendKeyUpdates; i++ { + if err := tlsConn.SendKeyUpdate(test.keyUpdateRequest); err != nil { + return err + } + } + + for i := 0; i < test.sendEmptyRecords; i++ { + tlsConn.Write(nil) + } + + for i := 0; i < test.sendWarningAlerts; i++ { + tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage) + } + + if test.sendHalfHelloRequest { + tlsConn.SendHalfHelloRequest() + } + + if test.renegotiate > 0 { + if test.renegotiateCiphers != nil { + config.CipherSuites = test.renegotiateCiphers + } + for i := 0; i < test.renegotiate; i++ { + if err := tlsConn.Renegotiate(); err != nil { + return err + } + } + } else if test.renegotiateCiphers != nil { + panic("renegotiateCiphers without renegotiate") + } + + if test.damageFirstWrite { + connDamage.setDamage(true) + tlsConn.Write([]byte("DAMAGED WRITE")) + connDamage.setDamage(false) + } + + messageLen := test.messageLen + if messageLen < 0 { + if test.protocol == dtls { + return fmt.Errorf("messageLen < 0 not supported for DTLS tests") + } + // Read until EOF. + _, err := io.Copy(ioutil.Discard, tlsConn) + return err + } + if messageLen == 0 { + messageLen = 32 + } + + messageCount := test.messageCount + if messageCount == 0 { + messageCount = 1 + } + + for j := 0; j < messageCount; j++ { + testMessage := make([]byte, messageLen) + for i := range testMessage { + testMessage[i] = 0x42 ^ byte(j) + } + tlsConn.Write(testMessage) + + for i := 0; i < test.sendKeyUpdates; i++ { + tlsConn.SendKeyUpdate(test.keyUpdateRequest) + } + + for i := 0; i < test.sendEmptyRecords; i++ { + tlsConn.Write(nil) + } + + for i := 0; i < test.sendWarningAlerts; i++ { + tlsConn.SendAlert(alertLevelWarning, alertUnexpectedMessage) + } + + if test.shimShutsDown || test.expectMessageDropped { + // The shim will not respond. + continue + } + + buf := make([]byte, len(testMessage)) + if test.protocol == dtls { + bufTmp := make([]byte, len(buf)+1) + n, err := tlsConn.Read(bufTmp) + if err != nil { + return err + } + if n != len(buf) { + return fmt.Errorf("bad reply; length mismatch (%d vs %d)", n, len(buf)) + } + copy(buf, bufTmp) + } else { + _, err := io.ReadFull(tlsConn, buf) + if err != nil { + return err + } + } + + for i, v := range buf { + if v != testMessage[i]^0xff { + return fmt.Errorf("bad reply contents at byte %d", i) + } + } + } + + return nil +} + +func valgrindOf(dbAttach bool, path string, args ...string) *exec.Cmd { + valgrindArgs := []string{"--error-exitcode=99", "--track-origins=yes", "--leak-check=full", "--quiet"} + if dbAttach { + valgrindArgs = append(valgrindArgs, "--db-attach=yes", "--db-command=xterm -e gdb -nw %f %p") + } + valgrindArgs = append(valgrindArgs, path) + valgrindArgs = append(valgrindArgs, args...) + + return exec.Command("valgrind", valgrindArgs...) +} + +func gdbOf(path string, args ...string) *exec.Cmd { + xtermArgs := []string{"-e", "gdb", "--args"} + xtermArgs = append(xtermArgs, path) + xtermArgs = append(xtermArgs, args...) + + return exec.Command("xterm", xtermArgs...) +} + +func lldbOf(path string, args ...string) *exec.Cmd { + xtermArgs := []string{"-e", "lldb", "--"} + xtermArgs = append(xtermArgs, path) + xtermArgs = append(xtermArgs, args...) + + return exec.Command("xterm", xtermArgs...) +} + +var ( + errMoreMallocs = errors.New("child process did not exhaust all allocation calls") + errUnimplemented = errors.New("child process does not implement needed flags") +) + +// accept accepts a connection from listener, unless waitChan signals a process +// exit first. +func acceptOrWait(listener net.Listener, waitChan chan error) (net.Conn, error) { + type connOrError struct { + conn net.Conn + err error + } + connChan := make(chan connOrError, 1) + go func() { + conn, err := listener.Accept() + connChan <- connOrError{conn, err} + close(connChan) + }() + select { + case result := <-connChan: + return result.conn, result.err + case childErr := <-waitChan: + waitChan <- childErr + return nil, fmt.Errorf("child exited early: %s", childErr) + } +} + +func translateExpectedError(errorStr string) string { + if translated, ok := shimConfig.ErrorMap[errorStr]; ok { + return translated + } + + if *looseErrors { + return "" + } + + return errorStr +} + +func runTest(test *testCase, shimPath string, mallocNumToFail int64) error { + // Help debugging panics on the Go side. + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "Test '%s' panicked.\n", test.name) + panic(r) + } + }() + + if !test.shouldFail && (len(test.expectedError) > 0 || len(test.expectedLocalError) > 0) { + panic("Error expected without shouldFail in " + test.name) + } + + if test.expectResumeRejected && !test.resumeSession { + panic("expectResumeRejected without resumeSession in " + test.name) + } + + for _, ver := range tlsVersions { + if !strings.Contains("-"+test.name+"-", "-"+ver.name+"-") { + continue + } + + if test.config.MaxVersion != 0 || test.config.MinVersion != 0 || test.expectedVersion != 0 { + continue + } + + panic(fmt.Sprintf("The name of test %q suggests that it's version specific, but min/max version in the Config is %x/%x. One of them should probably be %x", test.name, test.config.MinVersion, test.config.MaxVersion, ver.version)) + } + + listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}}) + if err != nil { + panic(err) + } + defer func() { + if listener != nil { + listener.Close() + } + }() + + flags := []string{"-port", strconv.Itoa(listener.Addr().(*net.TCPAddr).Port)} + if test.testType == serverTest { + flags = append(flags, "-server") + + flags = append(flags, "-key-file") + if test.keyFile == "" { + flags = append(flags, path.Join(*resourceDir, rsaKeyFile)) + } else { + flags = append(flags, path.Join(*resourceDir, test.keyFile)) + } + + flags = append(flags, "-cert-file") + if test.certFile == "" { + flags = append(flags, path.Join(*resourceDir, rsaCertificateFile)) + } else { + flags = append(flags, path.Join(*resourceDir, test.certFile)) + } + } + + if test.protocol == dtls { + flags = append(flags, "-dtls") + } + + var resumeCount int + if test.resumeSession { + resumeCount++ + if test.resumeRenewedSession { + resumeCount++ + } + } + + if resumeCount > 0 { + flags = append(flags, "-resume-count", strconv.Itoa(resumeCount)) + } + + if test.shimWritesFirst { + flags = append(flags, "-shim-writes-first") + } + + if test.shimShutsDown { + flags = append(flags, "-shim-shuts-down") + } + + if test.exportKeyingMaterial > 0 { + flags = append(flags, "-export-keying-material", strconv.Itoa(test.exportKeyingMaterial)) + flags = append(flags, "-export-label", test.exportLabel) + flags = append(flags, "-export-context", test.exportContext) + if test.useExportContext { + flags = append(flags, "-use-export-context") + } + } + if test.expectResumeRejected { + flags = append(flags, "-expect-session-miss") + } + + if test.testTLSUnique { + flags = append(flags, "-tls-unique") + } + + flags = append(flags, test.flags...) + + var shim *exec.Cmd + if *useValgrind { + shim = valgrindOf(false, shimPath, flags...) + } else if *useGDB { + shim = gdbOf(shimPath, flags...) + } else if *useLLDB { + shim = lldbOf(shimPath, flags...) + } else { + shim = exec.Command(shimPath, flags...) + } + shim.Stdin = os.Stdin + var stdoutBuf, stderrBuf bytes.Buffer + shim.Stdout = &stdoutBuf + shim.Stderr = &stderrBuf + if mallocNumToFail >= 0 { + shim.Env = os.Environ() + shim.Env = append(shim.Env, "MALLOC_NUMBER_TO_FAIL="+strconv.FormatInt(mallocNumToFail, 10)) + if *mallocTestDebug { + shim.Env = append(shim.Env, "MALLOC_BREAK_ON_FAIL=1") + } + shim.Env = append(shim.Env, "_MALLOC_CHECK=1") + } + + if err := shim.Start(); err != nil { + panic(err) + } + waitChan := make(chan error, 1) + go func() { waitChan <- shim.Wait() }() + + config := test.config + + if *deterministic { + config.Rand = &deterministicRand{} + } + + conn, err := acceptOrWait(listener, waitChan) + if err == nil { + err = doExchange(test, &config, conn, false /* not a resumption */, 0) + conn.Close() + } + + for i := 0; err == nil && i < resumeCount; i++ { + var resumeConfig Config + if test.resumeConfig != nil { + resumeConfig = *test.resumeConfig + if !test.newSessionsOnResume { + resumeConfig.SessionTicketKey = config.SessionTicketKey + resumeConfig.ClientSessionCache = config.ClientSessionCache + resumeConfig.ServerSessionCache = config.ServerSessionCache + } + resumeConfig.Rand = config.Rand + } else { + resumeConfig = config + } + var connResume net.Conn + connResume, err = acceptOrWait(listener, waitChan) + if err == nil { + err = doExchange(test, &resumeConfig, connResume, true /* resumption */, i+1) + connResume.Close() + } + } + + // Close the listener now. This is to avoid hangs should the shim try to + // open more connections than expected. + listener.Close() + listener = nil + + childErr := <-waitChan + var isValgrindError bool + if exitError, ok := childErr.(*exec.ExitError); ok { + switch exitError.Sys().(syscall.WaitStatus).ExitStatus() { + case 88: + return errMoreMallocs + case 89: + return errUnimplemented + case 99: + isValgrindError = true + } + } + + // Account for Windows line endings. + stdout := strings.Replace(string(stdoutBuf.Bytes()), "\r\n", "\n", -1) + stderr := strings.Replace(string(stderrBuf.Bytes()), "\r\n", "\n", -1) + + // Separate the errors from the shim and those from tools like + // AddressSanitizer. + var extraStderr string + if stderrParts := strings.SplitN(stderr, "--- DONE ---\n", 2); len(stderrParts) == 2 { + stderr = stderrParts[0] + extraStderr = stderrParts[1] + } + + failed := err != nil || childErr != nil + expectedError := translateExpectedError(test.expectedError) + correctFailure := len(expectedError) == 0 || strings.Contains(stderr, expectedError) + + localError := "none" + if err != nil { + localError = err.Error() + } + if len(test.expectedLocalError) != 0 { + correctFailure = correctFailure && strings.Contains(localError, test.expectedLocalError) + } + + if failed != test.shouldFail || failed && !correctFailure { + childError := "none" + if childErr != nil { + childError = childErr.Error() + } + + var msg string + switch { + case failed && !test.shouldFail: + msg = "unexpected failure" + case !failed && test.shouldFail: + msg = "unexpected success" + case failed && !correctFailure: + msg = "bad error (wanted '" + expectedError + "' / '" + test.expectedLocalError + "')" + default: + panic("internal error") + } + + return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s\n%s", msg, localError, childError, stdout, stderr, extraStderr) + } + + if len(extraStderr) > 0 || (!failed && len(stderr) > 0) { + return fmt.Errorf("unexpected error output:\n%s\n%s", stderr, extraStderr) + } + + if *useValgrind && isValgrindError { + return fmt.Errorf("valgrind error:\n%s\n%s", stderr, extraStderr) + } + + return nil +} + +type tlsVersion struct { + name string + version uint16 + flag string + hasDTLS bool +} + +var tlsVersions = []tlsVersion{ + {"SSL3", VersionSSL30, "-no-ssl3", false}, + {"TLS1", VersionTLS10, "-no-tls1", true}, + {"TLS11", VersionTLS11, "-no-tls11", false}, + {"TLS12", VersionTLS12, "-no-tls12", true}, + {"TLS13", VersionTLS13, "-no-tls13", false}, +} + +type testCipherSuite struct { + name string + id uint16 +} + +var testCipherSuites = []testCipherSuite{ + {"3DES-SHA", TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + {"AES128-GCM", TLS_RSA_WITH_AES_128_GCM_SHA256}, + {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA}, + {"AES128-SHA256", TLS_RSA_WITH_AES_128_CBC_SHA256}, + {"AES256-GCM", TLS_RSA_WITH_AES_256_GCM_SHA384}, + {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA}, + {"AES256-SHA256", TLS_RSA_WITH_AES_256_CBC_SHA256}, + {"DHE-RSA-AES128-GCM", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, + {"DHE-RSA-AES128-SHA", TLS_DHE_RSA_WITH_AES_128_CBC_SHA}, + {"DHE-RSA-AES128-SHA256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256}, + {"DHE-RSA-AES256-GCM", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384}, + {"DHE-RSA-AES256-SHA", TLS_DHE_RSA_WITH_AES_256_CBC_SHA}, + {"DHE-RSA-AES256-SHA256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256}, + {"ECDHE-ECDSA-AES128-GCM", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + {"ECDHE-ECDSA-AES128-SHA", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, + {"ECDHE-ECDSA-AES128-SHA256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, + {"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, + {"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + {"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384}, + {"ECDHE-ECDSA-CHACHA20-POLY1305", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256}, + {"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + {"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + {"ECDHE-RSA-AES128-SHA256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256}, + {"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, + {"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, + {"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384}, + {"ECDHE-RSA-CHACHA20-POLY1305", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, + {"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA}, + {"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA}, + {"ECDHE-PSK-AES128-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA}, + {"ECDHE-PSK-AES256-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA}, + {"ECDHE-PSK-CHACHA20-POLY1305", TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256}, + {"AEAD-CHACHA20-POLY1305", TLS_CHACHA20_POLY1305_SHA256}, + {"AEAD-AES128-GCM-SHA256", TLS_AES_128_GCM_SHA256}, + {"AEAD-AES256-GCM-SHA384", TLS_AES_256_GCM_SHA384}, + {"NULL-SHA", TLS_RSA_WITH_NULL_SHA}, +} + +func hasComponent(suiteName, component string) bool { + return strings.Contains("-"+suiteName+"-", "-"+component+"-") +} + +func isTLS12Only(suiteName string) bool { + return hasComponent(suiteName, "GCM") || + hasComponent(suiteName, "SHA256") || + hasComponent(suiteName, "SHA384") || + hasComponent(suiteName, "POLY1305") +} + +func isTLS13Suite(suiteName string) bool { + return strings.HasPrefix(suiteName, "AEAD-") +} + +func isDTLSCipher(suiteName string) bool { + return !hasComponent(suiteName, "RC4") && !hasComponent(suiteName, "NULL") +} + +func bigFromHex(hex string) *big.Int { + ret, ok := new(big.Int).SetString(hex, 16) + if !ok { + panic("failed to parse hex number 0x" + hex) + } + return ret +} + +func addBasicTests() { + basicTests := []testCase{ + { + name: "NoFallbackSCSV", + config: Config{ + Bugs: ProtocolBugs{ + FailIfNotFallbackSCSV: true, + }, + }, + shouldFail: true, + expectedLocalError: "no fallback SCSV found", + }, + { + name: "SendFallbackSCSV", + config: Config{ + Bugs: ProtocolBugs{ + FailIfNotFallbackSCSV: true, + }, + }, + flags: []string{"-fallback-scsv"}, + }, + { + name: "ClientCertificateTypes", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequestClientCert, + ClientCertificateTypes: []byte{ + CertTypeDSSSign, + CertTypeRSASign, + CertTypeECDSASign, + }, + }, + flags: []string{ + "-expect-certificate-types", + base64.StdEncoding.EncodeToString([]byte{ + CertTypeDSSSign, + CertTypeRSASign, + CertTypeECDSASign, + }), + }, + }, + { + name: "UnauthenticatedECDH", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + UnauthenticatedECDH: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + name: "SkipCertificateStatus", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SkipCertificateStatus: true, + }, + }, + flags: []string{ + "-enable-ocsp-stapling", + }, + }, + { + name: "SkipServerKeyExchange", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SkipServerKeyExchange: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + testType: serverTest, + name: "Alert", + config: Config{ + Bugs: ProtocolBugs{ + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }, + { + protocol: dtls, + testType: serverTest, + name: "Alert-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }, + { + testType: serverTest, + name: "FragmentAlert", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAlert: true, + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":BAD_ALERT:", + }, + { + protocol: dtls, + testType: serverTest, + name: "FragmentAlert-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAlert: true, + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":BAD_ALERT:", + }, + { + testType: serverTest, + name: "DoubleAlert", + config: Config{ + Bugs: ProtocolBugs{ + DoubleAlert: true, + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":BAD_ALERT:", + }, + { + protocol: dtls, + testType: serverTest, + name: "DoubleAlert-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + DoubleAlert: true, + SendSpuriousAlert: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":BAD_ALERT:", + }, + { + name: "SkipNewSessionTicket", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SkipNewSessionTicket: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + testType: serverTest, + name: "FallbackSCSV", + config: Config{ + MaxVersion: VersionTLS11, + Bugs: ProtocolBugs{ + SendFallbackSCSV: true, + }, + }, + shouldFail: true, + expectedError: ":INAPPROPRIATE_FALLBACK:", + expectedLocalError: "remote error: inappropriate fallback", + }, + { + testType: serverTest, + name: "FallbackSCSV-VersionMatch-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFallbackSCSV: true, + }, + }, + }, + { + testType: serverTest, + name: "FallbackSCSV-VersionMatch-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendFallbackSCSV: true, + }, + }, + flags: []string{"-max-version", strconv.Itoa(VersionTLS12)}, + }, + { + testType: serverTest, + name: "FragmentedClientVersion", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 1, + FragmentClientVersion: true, + }, + }, + expectedVersion: VersionTLS13, + }, + { + testType: serverTest, + name: "HttpGET", + sendPrefix: "GET / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpPOST", + sendPrefix: "POST / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpHEAD", + sendPrefix: "HEAD / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpPUT", + sendPrefix: "PUT / HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTP_REQUEST:", + }, + { + testType: serverTest, + name: "HttpCONNECT", + sendPrefix: "CONNECT www.google.com:443 HTTP/1.0\n", + shouldFail: true, + expectedError: ":HTTPS_PROXY_REQUEST:", + }, + { + testType: serverTest, + name: "Garbage", + sendPrefix: "blah", + shouldFail: true, + expectedError: ":WRONG_VERSION_NUMBER:", + }, + { + name: "RSAEphemeralKey", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + RSAEphemeralKey: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + name: "DisableEverything", + flags: []string{"-no-tls13", "-no-tls12", "-no-tls11", "-no-tls1", "-no-ssl3"}, + shouldFail: true, + expectedError: ":WRONG_SSL_VERSION:", + }, + { + protocol: dtls, + name: "DisableEverything-DTLS", + flags: []string{"-no-tls12", "-no-tls1"}, + shouldFail: true, + expectedError: ":WRONG_SSL_VERSION:", + }, + { + protocol: dtls, + testType: serverTest, + name: "MTU", + config: Config{ + Bugs: ProtocolBugs{ + MaxPacketLength: 256, + }, + }, + flags: []string{"-mtu", "256"}, + }, + { + protocol: dtls, + testType: serverTest, + name: "MTUExceeded", + config: Config{ + Bugs: ProtocolBugs{ + MaxPacketLength: 255, + }, + }, + flags: []string{"-mtu", "256"}, + shouldFail: true, + expectedLocalError: "dtls: exceeded maximum packet length", + }, + { + name: "CertMismatchRSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{ecdsaP256Certificate}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CERTIFICATE_TYPE:", + }, + { + name: "CertMismatchECDSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{rsaCertificate}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CERTIFICATE_TYPE:", + }, + { + name: "EmptyCertificateList", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + EmptyCertificateList: true, + }, + }, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }, + { + name: "EmptyCertificateList-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + EmptyCertificateList: true, + }, + }, + shouldFail: true, + expectedError: ":PEER_DID_NOT_RETURN_A_CERTIFICATE:", + }, + { + name: "TLSFatalBadPackets", + damageFirstWrite: true, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }, + { + protocol: dtls, + name: "DTLSIgnoreBadPackets", + damageFirstWrite: true, + }, + { + protocol: dtls, + name: "DTLSIgnoreBadPackets-Async", + damageFirstWrite: true, + flags: []string{"-async"}, + }, + { + name: "AppDataBeforeHandshake", + config: Config{ + Bugs: ProtocolBugs{ + AppDataBeforeHandshake: []byte("TEST MESSAGE"), + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "AppDataBeforeHandshake-Empty", + config: Config{ + Bugs: ProtocolBugs{ + AppDataBeforeHandshake: []byte{}, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + protocol: dtls, + name: "AppDataBeforeHandshake-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + AppDataBeforeHandshake: []byte("TEST MESSAGE"), + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + protocol: dtls, + name: "AppDataBeforeHandshake-DTLS-Empty", + config: Config{ + Bugs: ProtocolBugs{ + AppDataBeforeHandshake: []byte{}, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "AppDataAfterChangeCipherSpec", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "AppDataAfterChangeCipherSpec-Empty", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte{}, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + protocol: dtls, + name: "AppDataAfterChangeCipherSpec-DTLS", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), + }, + }, + // BoringSSL's DTLS implementation will drop the out-of-order + // application data. + }, + { + protocol: dtls, + name: "AppDataAfterChangeCipherSpec-DTLS-Empty", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte{}, + }, + }, + // BoringSSL's DTLS implementation will drop the out-of-order + // application data. + }, + { + name: "AlertAfterChangeCipherSpec", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + AlertAfterChangeCipherSpec: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }, + { + protocol: dtls, + name: "AlertAfterChangeCipherSpec-DTLS", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + AlertAfterChangeCipherSpec: alertRecordOverflow, + }, + }, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }, + { + protocol: dtls, + name: "ReorderHandshakeFragments-Small-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + ReorderHandshakeFragments: true, + // Small enough that every handshake message is + // fragmented. + MaxHandshakeRecordLength: 2, + }, + }, + }, + { + protocol: dtls, + name: "ReorderHandshakeFragments-Large-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + ReorderHandshakeFragments: true, + // Large enough that no handshake message is + // fragmented. + MaxHandshakeRecordLength: 2048, + }, + }, + }, + { + protocol: dtls, + name: "MixCompleteMessageWithFragments-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + ReorderHandshakeFragments: true, + MixCompleteMessageWithFragments: true, + MaxHandshakeRecordLength: 2, + }, + }, + }, + { + name: "SendInvalidRecordType", + config: Config{ + Bugs: ProtocolBugs{ + SendInvalidRecordType: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + protocol: dtls, + name: "SendInvalidRecordType-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendInvalidRecordType: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "FalseStart-SkipServerSecondLeg", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + SkipNewSessionTicket: true, + SkipChangeCipherSpec: true, + SkipFinished: true, + ExpectFalseStart: true, + }, + }, + flags: []string{ + "-false-start", + "-handshake-never-done", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + name: "FalseStart-SkipServerSecondLeg-Implicit", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + SkipNewSessionTicket: true, + SkipChangeCipherSpec: true, + SkipFinished: true, + }, + }, + flags: []string{ + "-implicit-handshake", + "-false-start", + "-handshake-never-done", + "-advertise-alpn", "\x03foo", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + testType: serverTest, + name: "FailEarlyCallback", + flags: []string{"-fail-early-callback"}, + shouldFail: true, + expectedError: ":CONNECTION_REJECTED:", + expectedLocalError: "remote error: handshake failure", + }, + { + name: "FailCertCallback-Client-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequestClientCert, + }, + flags: []string{"-fail-cert-callback"}, + shouldFail: true, + expectedError: ":CERT_CB_ERROR:", + expectedLocalError: "remote error: internal error", + }, + { + testType: serverTest, + name: "FailCertCallback-Server-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + }, + flags: []string{"-fail-cert-callback"}, + shouldFail: true, + expectedError: ":CERT_CB_ERROR:", + expectedLocalError: "remote error: internal error", + }, + { + name: "FailCertCallback-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequestClientCert, + }, + flags: []string{"-fail-cert-callback"}, + shouldFail: true, + expectedError: ":CERT_CB_ERROR:", + expectedLocalError: "remote error: internal error", + }, + { + testType: serverTest, + name: "FailCertCallback-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + }, + flags: []string{"-fail-cert-callback"}, + shouldFail: true, + expectedError: ":CERT_CB_ERROR:", + expectedLocalError: "remote error: internal error", + }, + { + protocol: dtls, + name: "FragmentMessageTypeMismatch-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 2, + FragmentMessageTypeMismatch: true, + }, + }, + shouldFail: true, + expectedError: ":FRAGMENT_MISMATCH:", + }, + { + protocol: dtls, + name: "FragmentMessageLengthMismatch-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 2, + FragmentMessageLengthMismatch: true, + }, + }, + shouldFail: true, + expectedError: ":FRAGMENT_MISMATCH:", + }, + { + protocol: dtls, + name: "SplitFragments-Header-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SplitFragments: 2, + }, + }, + shouldFail: true, + expectedError: ":BAD_HANDSHAKE_RECORD:", + }, + { + protocol: dtls, + name: "SplitFragments-Boundary-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SplitFragments: dtlsRecordHeaderLen, + }, + }, + shouldFail: true, + expectedError: ":BAD_HANDSHAKE_RECORD:", + }, + { + protocol: dtls, + name: "SplitFragments-Body-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SplitFragments: dtlsRecordHeaderLen + 1, + }, + }, + shouldFail: true, + expectedError: ":BAD_HANDSHAKE_RECORD:", + }, + { + protocol: dtls, + name: "SendEmptyFragments-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendEmptyFragments: true, + }, + }, + }, + { + name: "BadFinished-Client", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadFinished: true, + }, + }, + shouldFail: true, + expectedError: ":DIGEST_CHECK_FAILED:", + }, + { + name: "BadFinished-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + BadFinished: true, + }, + }, + shouldFail: true, + expectedError: ":DIGEST_CHECK_FAILED:", + }, + { + testType: serverTest, + name: "BadFinished-Server", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadFinished: true, + }, + }, + shouldFail: true, + expectedError: ":DIGEST_CHECK_FAILED:", + }, + { + testType: serverTest, + name: "BadFinished-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + BadFinished: true, + }, + }, + shouldFail: true, + expectedError: ":DIGEST_CHECK_FAILED:", + }, + { + name: "FalseStart-BadFinished", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + BadFinished: true, + ExpectFalseStart: true, + }, + }, + flags: []string{ + "-false-start", + "-handshake-never-done", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":DIGEST_CHECK_FAILED:", + }, + { + name: "NoFalseStart-NoALPN", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + AlertBeforeFalseStartTest: alertAccessDenied, + }, + }, + flags: []string{ + "-false-start", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", + expectedLocalError: "tls: peer did not false start: EOF", + }, + { + name: "NoFalseStart-NoAEAD", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + AlertBeforeFalseStartTest: alertAccessDenied, + }, + }, + flags: []string{ + "-false-start", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", + expectedLocalError: "tls: peer did not false start: EOF", + }, + { + name: "NoFalseStart-RSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + AlertBeforeFalseStartTest: alertAccessDenied, + }, + }, + flags: []string{ + "-false-start", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", + expectedLocalError: "tls: peer did not false start: EOF", + }, + { + name: "NoFalseStart-DHE_RSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + AlertBeforeFalseStartTest: alertAccessDenied, + }, + }, + flags: []string{ + "-false-start", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":TLSV1_ALERT_ACCESS_DENIED:", + expectedLocalError: "tls: peer did not false start: EOF", + }, + { + protocol: dtls, + name: "SendSplitAlert-Sync", + config: Config{ + Bugs: ProtocolBugs{ + SendSplitAlert: true, + }, + }, + }, + { + protocol: dtls, + name: "SendSplitAlert-Async", + config: Config{ + Bugs: ProtocolBugs{ + SendSplitAlert: true, + }, + }, + flags: []string{"-async"}, + }, + { + protocol: dtls, + name: "PackDTLSHandshake", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 2, + PackHandshakeFragments: 20, + PackHandshakeRecords: 200, + }, + }, + }, + { + name: "SendEmptyRecords-Pass", + sendEmptyRecords: 32, + }, + { + name: "SendEmptyRecords", + sendEmptyRecords: 33, + shouldFail: true, + expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:", + }, + { + name: "SendEmptyRecords-Async", + sendEmptyRecords: 33, + flags: []string{"-async"}, + shouldFail: true, + expectedError: ":TOO_MANY_EMPTY_FRAGMENTS:", + }, + { + name: "SendWarningAlerts-Pass", + config: Config{ + MaxVersion: VersionTLS12, + }, + sendWarningAlerts: 4, + }, + { + protocol: dtls, + name: "SendWarningAlerts-DTLS-Pass", + config: Config{ + MaxVersion: VersionTLS12, + }, + sendWarningAlerts: 4, + }, + { + name: "SendWarningAlerts-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + }, + sendWarningAlerts: 4, + shouldFail: true, + expectedError: ":BAD_ALERT:", + expectedLocalError: "remote error: error decoding message", + }, + { + name: "SendWarningAlerts", + config: Config{ + MaxVersion: VersionTLS12, + }, + sendWarningAlerts: 5, + shouldFail: true, + expectedError: ":TOO_MANY_WARNING_ALERTS:", + }, + { + name: "SendWarningAlerts-Async", + config: Config{ + MaxVersion: VersionTLS12, + }, + sendWarningAlerts: 5, + flags: []string{"-async"}, + shouldFail: true, + expectedError: ":TOO_MANY_WARNING_ALERTS:", + }, + { + name: "TooManyKeyUpdates", + config: Config{ + MaxVersion: VersionTLS13, + }, + sendKeyUpdates: 33, + keyUpdateRequest: keyUpdateNotRequested, + shouldFail: true, + expectedError: ":TOO_MANY_KEY_UPDATES:", + }, + { + name: "EmptySessionID", + config: Config{ + MaxVersion: VersionTLS12, + SessionTicketsDisabled: true, + }, + noSessionCache: true, + flags: []string{"-expect-no-session"}, + }, + { + name: "Unclean-Shutdown", + config: Config{ + Bugs: ProtocolBugs{ + NoCloseNotify: true, + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + flags: []string{"-check-close-notify"}, + shouldFail: true, + expectedError: "Unexpected SSL_shutdown result: -1 != 1", + }, + { + name: "Unclean-Shutdown-Ignored", + config: Config{ + Bugs: ProtocolBugs{ + NoCloseNotify: true, + }, + }, + shimShutsDown: true, + }, + { + name: "Unclean-Shutdown-Alert", + config: Config{ + Bugs: ProtocolBugs{ + SendAlertOnShutdown: alertDecompressionFailure, + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + flags: []string{"-check-close-notify"}, + shouldFail: true, + expectedError: ":SSLV3_ALERT_DECOMPRESSION_FAILURE:", + }, + { + name: "LargePlaintext", + config: Config{ + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + messageLen: maxPlaintext + 1, + shouldFail: true, + expectedError: ":DATA_LENGTH_TOO_LONG:", + }, + { + protocol: dtls, + name: "LargePlaintext-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + messageLen: maxPlaintext + 1, + shouldFail: true, + expectedError: ":DATA_LENGTH_TOO_LONG:", + }, + { + name: "LargeCiphertext", + config: Config{ + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + messageLen: maxPlaintext * 2, + shouldFail: true, + expectedError: ":ENCRYPTED_LENGTH_TOO_LONG:", + }, + { + protocol: dtls, + name: "LargeCiphertext-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendLargeRecords: true, + }, + }, + messageLen: maxPlaintext * 2, + // Unlike the other four cases, DTLS drops records which + // are invalid before authentication, so the connection + // does not fail. + expectMessageDropped: true, + }, + { + name: "BadHelloRequest-1", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadHelloRequest: []byte{typeHelloRequest, 0, 0, 1, 1}, + }, + }, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + shouldFail: true, + expectedError: ":EXCESSIVE_MESSAGE_SIZE:", + }, + { + name: "BadHelloRequest-2", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadHelloRequest: []byte{typeServerKeyExchange, 0, 0, 0}, + }, + }, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + shouldFail: true, + expectedError: ":BAD_HELLO_REQUEST:", + }, + { + testType: serverTest, + name: "SupportTicketsWithSessionID", + config: Config{ + MaxVersion: VersionTLS12, + SessionTicketsDisabled: true, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS12, + }, + resumeSession: true, + }, + { + protocol: dtls, + name: "DTLS-SendExtraFinished", + config: Config{ + Bugs: ProtocolBugs{ + SendExtraFinished: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + protocol: dtls, + name: "DTLS-SendExtraFinished-Reordered", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 2, + ReorderHandshakeFragments: true, + SendExtraFinished: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }, + { + testType: serverTest, + name: "V2ClientHello-EmptyRecordPrefix", + config: Config{ + // Choose a cipher suite that does not involve + // elliptic curves, so no extensions are + // involved. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + Bugs: ProtocolBugs{ + SendV2ClientHello: true, + }, + }, + sendPrefix: string([]byte{ + byte(recordTypeHandshake), + 3, 1, // version + 0, 0, // length + }), + // A no-op empty record may not be sent before V2ClientHello. + shouldFail: true, + expectedError: ":WRONG_VERSION_NUMBER:", + }, + { + testType: serverTest, + name: "V2ClientHello-WarningAlertPrefix", + config: Config{ + // Choose a cipher suite that does not involve + // elliptic curves, so no extensions are + // involved. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + Bugs: ProtocolBugs{ + SendV2ClientHello: true, + }, + }, + sendPrefix: string([]byte{ + byte(recordTypeAlert), + 3, 1, // version + 0, 2, // length + alertLevelWarning, byte(alertDecompressionFailure), + }), + // A no-op warning alert may not be sent before V2ClientHello. + shouldFail: true, + expectedError: ":WRONG_VERSION_NUMBER:", + }, + { + name: "KeyUpdate-Client", + config: Config{ + MaxVersion: VersionTLS13, + }, + sendKeyUpdates: 1, + keyUpdateRequest: keyUpdateNotRequested, + }, + { + testType: serverTest, + name: "KeyUpdate-Server", + config: Config{ + MaxVersion: VersionTLS13, + }, + sendKeyUpdates: 1, + keyUpdateRequest: keyUpdateNotRequested, + }, + { + name: "KeyUpdate-InvalidRequestMode", + config: Config{ + MaxVersion: VersionTLS13, + }, + sendKeyUpdates: 1, + keyUpdateRequest: 42, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }, + { + name: "SendSNIWarningAlert", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendSNIWarningAlert: true, + }, + }, + }, + { + testType: serverTest, + name: "ExtraCompressionMethods-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendCompressionMethods: []byte{1, 2, 3, compressionNone, 4, 5, 6}, + }, + }, + }, + { + testType: serverTest, + name: "ExtraCompressionMethods-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendCompressionMethods: []byte{1, 2, 3, compressionNone, 4, 5, 6}, + }, + }, + shouldFail: true, + expectedError: ":INVALID_COMPRESSION_LIST:", + expectedLocalError: "remote error: illegal parameter", + }, + { + testType: serverTest, + name: "NoNullCompression-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendCompressionMethods: []byte{1, 2, 3, 4, 5, 6}, + }, + }, + shouldFail: true, + expectedError: ":INVALID_COMPRESSION_LIST:", + expectedLocalError: "remote error: illegal parameter", + }, + { + testType: serverTest, + name: "NoNullCompression-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendCompressionMethods: []byte{1, 2, 3, 4, 5, 6}, + }, + }, + shouldFail: true, + expectedError: ":INVALID_COMPRESSION_LIST:", + expectedLocalError: "remote error: illegal parameter", + }, + { + name: "GREASE-Client-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + ExpectGREASE: true, + }, + }, + flags: []string{"-enable-grease"}, + }, + { + name: "GREASE-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExpectGREASE: true, + }, + }, + flags: []string{"-enable-grease"}, + }, + { + testType: serverTest, + name: "GREASE-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + // TLS 1.3 servers are expected to + // always enable GREASE. TLS 1.3 is new, + // so there is no existing ecosystem to + // worry about. + ExpectGREASE: true, + }, + }, + }, + { + // Test the server so there is a large certificate as + // well as application data. + testType: serverTest, + name: "MaxSendFragment", + config: Config{ + Bugs: ProtocolBugs{ + MaxReceivePlaintext: 512, + }, + }, + messageLen: 1024, + flags: []string{ + "-max-send-fragment", "512", + "-read-size", "1024", + }, + }, + { + // Test the server so there is a large certificate as + // well as application data. + testType: serverTest, + name: "MaxSendFragment-TooLarge", + config: Config{ + Bugs: ProtocolBugs{ + // Ensure that some of the records are + // 512. + MaxReceivePlaintext: 511, + }, + }, + messageLen: 1024, + flags: []string{ + "-max-send-fragment", "512", + "-read-size", "1024", + }, + shouldFail: true, + expectedLocalError: "local error: record overflow", + }, + } + testCases = append(testCases, basicTests...) + + // Test that very large messages can be received. + cert := rsaCertificate + for i := 0; i < 50; i++ { + cert.Certificate = append(cert.Certificate, cert.Certificate[0]) + } + testCases = append(testCases, testCase{ + name: "LargeMessage", + config: Config{ + Certificates: []Certificate{cert}, + }, + }) + testCases = append(testCases, testCase{ + protocol: dtls, + name: "LargeMessage-DTLS", + config: Config{ + Certificates: []Certificate{cert}, + }, + }) + + // They are rejected if the maximum certificate chain length is capped. + testCases = append(testCases, testCase{ + name: "LargeMessage-Reject", + config: Config{ + Certificates: []Certificate{cert}, + }, + flags: []string{"-max-cert-list", "16384"}, + shouldFail: true, + expectedError: ":EXCESSIVE_MESSAGE_SIZE:", + }) + testCases = append(testCases, testCase{ + protocol: dtls, + name: "LargeMessage-Reject-DTLS", + config: Config{ + Certificates: []Certificate{cert}, + }, + flags: []string{"-max-cert-list", "16384"}, + shouldFail: true, + expectedError: ":EXCESSIVE_MESSAGE_SIZE:", + }) +} + +func addTestForCipherSuite(suite testCipherSuite, ver tlsVersion, protocol protocol) { + const psk = "12345" + const pskIdentity = "luggage combo" + + var prefix string + if protocol == dtls { + if !ver.hasDTLS { + return + } + prefix = "D" + } + + var cert Certificate + var certFile string + var keyFile string + if hasComponent(suite.name, "ECDSA") { + cert = ecdsaP256Certificate + certFile = ecdsaP256CertificateFile + keyFile = ecdsaP256KeyFile + } else { + cert = rsaCertificate + certFile = rsaCertificateFile + keyFile = rsaKeyFile + } + + var flags []string + if hasComponent(suite.name, "PSK") { + flags = append(flags, + "-psk", psk, + "-psk-identity", pskIdentity) + } + if hasComponent(suite.name, "NULL") { + // NULL ciphers must be explicitly enabled. + flags = append(flags, "-cipher", "DEFAULT:NULL-SHA") + } + + var shouldServerFail, shouldClientFail bool + if hasComponent(suite.name, "ECDHE") && ver.version == VersionSSL30 { + // BoringSSL clients accept ECDHE on SSLv3, but + // a BoringSSL server will never select it + // because the extension is missing. + shouldServerFail = true + } + if isTLS12Only(suite.name) && ver.version < VersionTLS12 { + shouldClientFail = true + shouldServerFail = true + } + if !isTLS13Suite(suite.name) && ver.version >= VersionTLS13 { + shouldClientFail = true + shouldServerFail = true + } + if isTLS13Suite(suite.name) && ver.version < VersionTLS13 { + shouldClientFail = true + shouldServerFail = true + } + if !isDTLSCipher(suite.name) && protocol == dtls { + shouldClientFail = true + shouldServerFail = true + } + + var sendCipherSuite uint16 + var expectedServerError, expectedClientError string + serverCipherSuites := []uint16{suite.id} + if shouldServerFail { + expectedServerError = ":NO_SHARED_CIPHER:" + } + if shouldClientFail { + expectedClientError = ":WRONG_CIPHER_RETURNED:" + // Configure the server to select ciphers as normal but + // select an incompatible cipher in ServerHello. + serverCipherSuites = nil + sendCipherSuite = suite.id + } + + // For cipher suites and versions where exporters are defined, verify + // that they interoperate. + var exportKeyingMaterial int + if ver.version > VersionSSL30 { + exportKeyingMaterial = 1024 + } + + testCases = append(testCases, testCase{ + testType: serverTest, + protocol: protocol, + name: prefix + ver.name + "-" + suite.name + "-server", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + Bugs: ProtocolBugs{ + AdvertiseAllConfiguredCiphers: true, + }, + }, + certFile: certFile, + keyFile: keyFile, + flags: flags, + resumeSession: true, + shouldFail: shouldServerFail, + expectedError: expectedServerError, + exportKeyingMaterial: exportKeyingMaterial, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + protocol: protocol, + name: prefix + ver.name + "-" + suite.name + "-client", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + CipherSuites: serverCipherSuites, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + Bugs: ProtocolBugs{ + IgnorePeerCipherPreferences: shouldClientFail, + SendCipherSuite: sendCipherSuite, + }, + }, + flags: flags, + resumeSession: true, + shouldFail: shouldClientFail, + expectedError: expectedClientError, + exportKeyingMaterial: exportKeyingMaterial, + }) + + if shouldClientFail { + return + } + + // Ensure the maximum record size is accepted. + testCases = append(testCases, testCase{ + protocol: protocol, + name: prefix + ver.name + "-" + suite.name + "-LargeRecord", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + }, + flags: flags, + messageLen: maxPlaintext, + }) + + // Test bad records for all ciphers. Bad records are fatal in TLS + // and ignored in DTLS. + var shouldFail bool + var expectedError string + if protocol == tls { + shouldFail = true + expectedError = ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" + } + + testCases = append(testCases, testCase{ + protocol: protocol, + name: prefix + ver.name + "-" + suite.name + "-BadRecord", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + }, + flags: flags, + damageFirstWrite: true, + messageLen: maxPlaintext, + shouldFail: shouldFail, + expectedError: expectedError, + }) +} + +func addCipherSuiteTests() { + const bogusCipher = 0xfe00 + + for _, suite := range testCipherSuites { + for _, ver := range tlsVersions { + for _, protocol := range []protocol{tls, dtls} { + addTestForCipherSuite(suite, ver, protocol) + } + } + } + + testCases = append(testCases, testCase{ + name: "NoSharedCipher", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{}, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:", + }) + + testCases = append(testCases, testCase{ + name: "NoSharedCipher-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{}, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:", + }) + + testCases = append(testCases, testCase{ + name: "UnsupportedCipherSuite", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + IgnorePeerCipherPreferences: true, + }, + }, + flags: []string{"-cipher", "DEFAULT:!AES"}, + shouldFail: true, + expectedError: ":WRONG_CIPHER_RETURNED:", + }) + + testCases = append(testCases, testCase{ + name: "ServerHelloBogusCipher", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendCipherSuite: bogusCipher, + }, + }, + shouldFail: true, + expectedError: ":UNKNOWN_CIPHER_RETURNED:", + }) + testCases = append(testCases, testCase{ + name: "ServerHelloBogusCipher-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendCipherSuite: bogusCipher, + }, + }, + shouldFail: true, + expectedError: ":UNKNOWN_CIPHER_RETURNED:", + }) + + testCases = append(testCases, testCase{ + name: "WeakDH", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + // This is a 1023-bit prime number, generated + // with: + // openssl gendh 1023 | openssl asn1parse -i + DHGroupPrime: bigFromHex("518E9B7930CE61C6E445C8360584E5FC78D9137C0FFDC880B495D5338ADF7689951A6821C17A76B3ACB8E0156AEA607B7EC406EBEDBB84D8376EB8FE8F8BA1433488BEE0C3EDDFD3A32DBB9481980A7AF6C96BFCF490A094CFFB2B8192C1BB5510B77B658436E27C2D4D023FE3718222AB0CA1273995B51F6D625A4944D0DD4B"), + }, + }, + shouldFail: true, + expectedError: ":BAD_DH_P_LENGTH:", + }) + + testCases = append(testCases, testCase{ + name: "SillyDH", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + // This is a 4097-bit prime number, generated + // with: + // openssl gendh 4097 | openssl asn1parse -i + DHGroupPrime: bigFromHex("01D366FA64A47419B0CD4A45918E8D8C8430F674621956A9F52B0CA592BC104C6E38D60C58F2CA66792A2B7EBDC6F8FFE75AB7D6862C261F34E96A2AEEF53AB7C21365C2E8FB0582F71EB57B1C227C0E55AE859E9904A25EFECD7B435C4D4357BD840B03649D4A1F8037D89EA4E1967DBEEF1CC17A6111C48F12E9615FFF336D3F07064CB17C0B765A012C850B9E3AA7A6984B96D8C867DDC6D0F4AB52042572244796B7ECFF681CD3B3E2E29AAECA391A775BEE94E502FB15881B0F4AC60314EA947C0C82541C3D16FD8C0E09BB7F8F786582032859D9C13187CE6C0CB6F2D3EE6C3C9727C15F14B21D3CD2E02BDB9D119959B0E03DC9E5A91E2578762300B1517D2352FC1D0BB934A4C3E1B20CE9327DB102E89A6C64A8C3148EDFC5A94913933853442FA84451B31FD21E492F92DD5488E0D871AEBFE335A4B92431DEC69591548010E76A5B365D346786E9A2D3E589867D796AA5E25211201D757560D318A87DFB27F3E625BC373DB48BF94A63161C674C3D4265CB737418441B7650EABC209CF675A439BEB3E9D1AA1B79F67198A40CEFD1C89144F7D8BAF61D6AD36F466DA546B4174A0E0CAF5BD788C8243C7C2DDDCC3DB6FC89F12F17D19FBD9B0BC76FE92891CD6BA07BEA3B66EF12D0D85E788FD58675C1B0FBD16029DCC4D34E7A1A41471BDEDF78BF591A8B4E96D88BEC8EDC093E616292BFC096E69A916E8D624B"), + }, + }, + shouldFail: true, + expectedError: ":DH_P_TOO_LONG:", + }) + + // This test ensures that Diffie-Hellman public values are padded with + // zeros so that they're the same length as the prime. This is to avoid + // hitting a bug in yaSSL. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "DHPublicValuePadded", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + RequireDHPublicValueLen: (1025 + 7) / 8, + }, + }, + flags: []string{"-use-sparse-dh-prime"}, + }) + + // The server must be tolerant to bogus ciphers. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "UnknownCipher", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{bogusCipher, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + AdvertiseAllConfiguredCiphers: true, + }, + }, + }) + + // The server must be tolerant to bogus ciphers. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "UnknownCipher-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{bogusCipher, TLS_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + AdvertiseAllConfiguredCiphers: true, + }, + }, + }) + + // Test empty ECDHE_PSK identity hints work as expected. + testCases = append(testCases, testCase{ + name: "EmptyECDHEPSKHint", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA}, + PreSharedKey: []byte("secret"), + }, + flags: []string{"-psk", "secret"}, + }) + + // Test empty PSK identity hints work as expected, even if an explicit + // ServerKeyExchange is sent. + testCases = append(testCases, testCase{ + name: "ExplicitEmptyPSKHint", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, + PreSharedKey: []byte("secret"), + Bugs: ProtocolBugs{ + AlwaysSendPreSharedKeyIdentityHint: true, + }, + }, + flags: []string{"-psk", "secret"}, + }) +} + +func addBadECDSASignatureTests() { + for badR := BadValue(1); badR < NumBadValues; badR++ { + for badS := BadValue(1); badS < NumBadValues; badS++ { + testCases = append(testCases, testCase{ + name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS), + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{ecdsaP256Certificate}, + Bugs: ProtocolBugs{ + BadECDSAR: badR, + BadECDSAS: badS, + }, + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + }) + testCases = append(testCases, testCase{ + name: fmt.Sprintf("BadECDSA-%d-%d-TLS13", badR, badS), + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{ecdsaP256Certificate}, + Bugs: ProtocolBugs{ + BadECDSAR: badR, + BadECDSAS: badS, + }, + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + }) + } + } +} + +func addCBCPaddingTests() { + testCases = append(testCases, testCase{ + name: "MaxCBCPadding", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + MaxPadding: true, + }, + }, + messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size + }) + testCases = append(testCases, testCase{ + name: "BadCBCPadding", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + PaddingFirstByteBad: true, + }, + }, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }) + // OpenSSL previously had an issue where the first byte of padding in + // 255 bytes of padding wasn't checked. + testCases = append(testCases, testCase{ + name: "BadCBCPadding255", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + MaxPadding: true, + PaddingFirstByteBadIf255: true, + }, + }, + messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }) +} + +func addCBCSplittingTests() { + testCases = append(testCases, testCase{ + name: "CBCRecordSplitting", + config: Config{ + MaxVersion: VersionTLS10, + MinVersion: VersionTLS10, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + }, + messageLen: -1, // read until EOF + resumeSession: true, + flags: []string{ + "-async", + "-write-different-record-sizes", + "-cbc-record-splitting", + }, + }) + testCases = append(testCases, testCase{ + name: "CBCRecordSplittingPartialWrite", + config: Config{ + MaxVersion: VersionTLS10, + MinVersion: VersionTLS10, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + }, + messageLen: -1, // read until EOF + flags: []string{ + "-async", + "-write-different-record-sizes", + "-cbc-record-splitting", + "-partial-write", + }, + }) +} + +func addClientAuthTests() { + // Add a dummy cert pool to stress certificate authority parsing. + certPool := x509.NewCertPool() + for _, cert := range []Certificate{rsaCertificate, rsa1024Certificate} { + cert, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + panic(err) + } + certPool.AddCert(cert) + } + caNames := certPool.Subjects() + + for _, ver := range tlsVersions { + testCases = append(testCases, testCase{ + testType: clientTest, + name: ver.name + "-Client-ClientAuth-RSA", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + ClientAuth: RequireAnyClientCert, + ClientCAs: certPool, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: ver.name + "-Server-ClientAuth-RSA", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{"-require-any-client-certificate"}, + }) + if ver.version != VersionSSL30 { + testCases = append(testCases, testCase{ + testType: serverTest, + name: ver.name + "-Server-ClientAuth-ECDSA", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{ecdsaP256Certificate}, + }, + flags: []string{"-require-any-client-certificate"}, + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: ver.name + "-Client-ClientAuth-ECDSA", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + ClientAuth: RequireAnyClientCert, + ClientCAs: certPool, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile), + }, + }) + } + + testCases = append(testCases, testCase{ + name: "NoClientCertificate-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + ClientAuth: RequireAnyClientCert, + }, + shouldFail: true, + expectedLocalError: "client didn't provide a certificate", + }) + + testCases = append(testCases, testCase{ + // Even if not configured to expect a certificate, OpenSSL will + // return X509_V_OK as the verify_result. + testType: serverTest, + name: "NoClientCertificateRequested-Server-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + }, + flags: []string{ + "-expect-verify-result", + }, + resumeSession: true, + }) + + testCases = append(testCases, testCase{ + // If a client certificate is not provided, OpenSSL will still + // return X509_V_OK as the verify_result. + testType: serverTest, + name: "NoClientCertificate-Server-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + }, + flags: []string{ + "-expect-verify-result", + "-verify-peer", + }, + resumeSession: true, + }) + + certificateRequired := "remote error: certificate required" + if ver.version < VersionTLS13 { + // Prior to TLS 1.3, the generic handshake_failure alert + // was used. + certificateRequired = "remote error: handshake failure" + } + testCases = append(testCases, testCase{ + testType: serverTest, + name: "RequireAnyClientCertificate-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + }, + flags: []string{"-require-any-client-certificate"}, + shouldFail: true, + expectedError: ":PEER_DID_NOT_RETURN_A_CERTIFICATE:", + expectedLocalError: certificateRequired, + }) + + if ver.version != VersionSSL30 { + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipClientCertificate-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SkipClientCertificate: true, + }, + }, + // Setting SSL_VERIFY_PEER allows anonymous clients. + flags: []string{"-verify-peer"}, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }) + } + + testCases = append(testCases, testCase{ + testType: serverTest, + name: ver.name + "-Server-CertReq-CA-List", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{rsaCertificate}, + Bugs: ProtocolBugs{ + ExpectCertificateReqNames: caNames, + }, + }, + flags: []string{ + "-require-any-client-certificate", + "-use-client-ca-list", encodeDERValues(caNames), + }, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: ver.name + "-Client-CertReq-CA-List", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{rsaCertificate}, + ClientAuth: RequireAnyClientCert, + ClientCAs: certPool, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-expect-client-ca-list", encodeDERValues(caNames), + }, + }) + } + + // Client auth is only legal in certificate-based ciphers. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ClientAuth-PSK", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, + PreSharedKey: []byte("secret"), + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-psk", "secret", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ClientAuth-ECDHE_PSK", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA}, + PreSharedKey: []byte("secret"), + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-psk", "secret", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }) + + // Regression test for a bug where the client CA list, if explicitly + // set to NULL, was mis-encoded. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Null-Client-CA-List", + config: Config{ + MaxVersion: VersionTLS12, + Certificates: []Certificate{rsaCertificate}, + Bugs: ProtocolBugs{ + ExpectCertificateReqNames: [][]byte{}, + }, + }, + flags: []string{ + "-require-any-client-certificate", + "-use-client-ca-list", "<NULL>", + }, + }) +} + +func addExtendedMasterSecretTests() { + const expectEMSFlag = "-expect-extended-master-secret" + + for _, with := range []bool{false, true} { + prefix := "No" + if with { + prefix = "" + } + + for _, isClient := range []bool{false, true} { + suffix := "-Server" + testType := serverTest + if isClient { + suffix = "-Client" + testType = clientTest + } + + for _, ver := range tlsVersions { + // In TLS 1.3, the extension is irrelevant and + // always reports as enabled. + var flags []string + if with || ver.version >= VersionTLS13 { + flags = []string{expectEMSFlag} + } + + test := testCase{ + testType: testType, + name: prefix + "ExtendedMasterSecret-" + ver.name + suffix, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: !with, + RequireExtendedMasterSecret: with, + }, + }, + flags: flags, + shouldFail: ver.version == VersionSSL30 && with, + } + if test.shouldFail { + test.expectedLocalError = "extended master secret required but not supported by peer" + } + testCases = append(testCases, test) + } + } + } + + for _, isClient := range []bool{false, true} { + for _, supportedInFirstConnection := range []bool{false, true} { + for _, supportedInResumeConnection := range []bool{false, true} { + boolToWord := func(b bool) string { + if b { + return "Yes" + } + return "No" + } + suffix := boolToWord(supportedInFirstConnection) + "To" + boolToWord(supportedInResumeConnection) + "-" + if isClient { + suffix += "Client" + } else { + suffix += "Server" + } + + supportedConfig := Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + RequireExtendedMasterSecret: true, + }, + } + + noSupportConfig := Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: true, + }, + } + + test := testCase{ + name: "ExtendedMasterSecret-" + suffix, + resumeSession: true, + } + + if !isClient { + test.testType = serverTest + } + + if supportedInFirstConnection { + test.config = supportedConfig + } else { + test.config = noSupportConfig + } + + if supportedInResumeConnection { + test.resumeConfig = &supportedConfig + } else { + test.resumeConfig = &noSupportConfig + } + + switch suffix { + case "YesToYes-Client", "YesToYes-Server": + // When a session is resumed, it should + // still be aware that its master + // secret was generated via EMS and + // thus it's safe to use tls-unique. + test.flags = []string{expectEMSFlag} + case "NoToYes-Server": + // If an original connection did not + // contain EMS, but a resumption + // handshake does, then a server should + // not resume the session. + test.expectResumeRejected = true + case "YesToNo-Server": + // Resuming an EMS session without the + // EMS extension should cause the + // server to abort the connection. + test.shouldFail = true + test.expectedError = ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" + case "NoToYes-Client": + // A client should abort a connection + // where the server resumed a non-EMS + // session but echoed the EMS + // extension. + test.shouldFail = true + test.expectedError = ":RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION:" + case "YesToNo-Client": + // A client should abort a connection + // where the server didn't echo EMS + // when the session used it. + test.shouldFail = true + test.expectedError = ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" + } + + testCases = append(testCases, test) + } + } + } + + // Switching EMS on renegotiation is forbidden. + testCases = append(testCases, testCase{ + name: "ExtendedMasterSecret-Renego-NoEMS", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: true, + NoExtendedMasterSecretOnRenegotiation: true, + }, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + }) + + testCases = append(testCases, testCase{ + name: "ExtendedMasterSecret-Renego-Upgrade", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: true, + }, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + shouldFail: true, + expectedError: ":RENEGOTIATION_EMS_MISMATCH:", + }) + + testCases = append(testCases, testCase{ + name: "ExtendedMasterSecret-Renego-Downgrade", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoExtendedMasterSecretOnRenegotiation: true, + }, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + shouldFail: true, + expectedError: ":RENEGOTIATION_EMS_MISMATCH:", + }) +} + +type stateMachineTestConfig struct { + protocol protocol + async bool + splitHandshake, packHandshakeFlight bool +} + +// Adds tests that try to cover the range of the handshake state machine, under +// various conditions. Some of these are redundant with other tests, but they +// only cover the synchronous case. +func addAllStateMachineCoverageTests() { + for _, async := range []bool{false, true} { + for _, protocol := range []protocol{tls, dtls} { + addStateMachineCoverageTests(stateMachineTestConfig{ + protocol: protocol, + async: async, + }) + addStateMachineCoverageTests(stateMachineTestConfig{ + protocol: protocol, + async: async, + splitHandshake: true, + }) + if protocol == tls { + addStateMachineCoverageTests(stateMachineTestConfig{ + protocol: protocol, + async: async, + packHandshakeFlight: true, + }) + } + } + } +} + +func addStateMachineCoverageTests(config stateMachineTestConfig) { + var tests []testCase + + // Basic handshake, with resumption. Client and server, + // session ID and session ticket. + tests = append(tests, testCase{ + name: "Basic-Client", + config: Config{ + MaxVersion: VersionTLS12, + }, + resumeSession: true, + // Ensure session tickets are used, not session IDs. + noSessionCache: true, + }) + tests = append(tests, testCase{ + name: "Basic-Client-RenewTicket", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + RenewTicketOnResume: true, + }, + }, + flags: []string{"-expect-ticket-renewal"}, + resumeSession: true, + resumeRenewedSession: true, + }) + tests = append(tests, testCase{ + name: "Basic-Client-NoTicket", + config: Config{ + MaxVersion: VersionTLS12, + SessionTicketsDisabled: true, + }, + resumeSession: true, + }) + tests = append(tests, testCase{ + name: "Basic-Client-Implicit", + config: Config{ + MaxVersion: VersionTLS12, + }, + flags: []string{"-implicit-handshake"}, + resumeSession: true, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + RequireSessionTickets: true, + }, + }, + resumeSession: true, + flags: []string{"-expect-no-session-id"}, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-NoTickets", + config: Config{ + MaxVersion: VersionTLS12, + SessionTicketsDisabled: true, + }, + resumeSession: true, + flags: []string{"-expect-session-id"}, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-Implicit", + config: Config{ + MaxVersion: VersionTLS12, + }, + flags: []string{"-implicit-handshake"}, + resumeSession: true, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-EarlyCallback", + config: Config{ + MaxVersion: VersionTLS12, + }, + flags: []string{"-use-early-callback"}, + resumeSession: true, + }) + + // TLS 1.3 basic handshake shapes. + if config.protocol == tls { + tests = append(tests, testCase{ + name: "TLS13-1RTT-Client", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + }, + resumeSession: true, + resumeRenewedSession: true, + }) + + tests = append(tests, testCase{ + testType: serverTest, + name: "TLS13-1RTT-Server", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + }, + resumeSession: true, + resumeRenewedSession: true, + // TLS 1.3 uses tickets, so the session should not be + // cached statefully. + flags: []string{"-expect-no-session-id"}, + }) + + tests = append(tests, testCase{ + name: "TLS13-HelloRetryRequest-Client", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + // P-384 requires a HelloRetryRequest against BoringSSL's default + // configuration. Assert this with ExpectMissingKeyShare. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + ExpectMissingKeyShare: true, + }, + }, + // Cover HelloRetryRequest during an ECDHE-PSK resumption. + resumeSession: true, + }) + + tests = append(tests, testCase{ + testType: serverTest, + name: "TLS13-HelloRetryRequest-Server", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + // Require a HelloRetryRequest for every curve. + DefaultCurves: []CurveID{}, + }, + // Cover HelloRetryRequest during an ECDHE-PSK resumption. + resumeSession: true, + }) + } + + // TLS client auth. + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-NoCertificate-Client", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequestClientCert, + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "ClientAuth-NoCertificate-Server", + config: Config{ + MaxVersion: VersionTLS12, + }, + // Setting SSL_VERIFY_PEER allows anonymous clients. + flags: []string{"-verify-peer"}, + }) + if config.protocol == tls { + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-NoCertificate-Client-SSL3", + config: Config{ + MaxVersion: VersionSSL30, + ClientAuth: RequestClientCert, + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "ClientAuth-NoCertificate-Server-SSL3", + config: Config{ + MaxVersion: VersionSSL30, + }, + // Setting SSL_VERIFY_PEER allows anonymous clients. + flags: []string{"-verify-peer"}, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-NoCertificate-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequestClientCert, + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "ClientAuth-NoCertificate-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + }, + // Setting SSL_VERIFY_PEER allows anonymous clients. + flags: []string{"-verify-peer"}, + }) + } + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-RSA-Client", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-RSA-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-ECDSA-Client", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile), + }, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-ECDSA-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile), + }, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-NoCertificate-OldCallback", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequestClientCert, + }, + flags: []string{"-use-old-client-cert-callback"}, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-NoCertificate-OldCallback-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequestClientCert, + }, + flags: []string{"-use-old-client-cert-callback"}, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-OldCallback", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-use-old-client-cert-callback", + }, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "ClientAuth-OldCallback-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-use-old-client-cert-callback", + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "ClientAuth-Server", + config: Config{ + MaxVersion: VersionTLS12, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{"-require-any-client-certificate"}, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "ClientAuth-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{"-require-any-client-certificate"}, + }) + + // Test each key exchange on the server side for async keys. + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-RSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-ECDHE-RSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-ECDHE-ECDSA", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile), + }, + }) + + // No session ticket support; server doesn't send NewSessionTicket. + tests = append(tests, testCase{ + name: "SessionTicketsDisabled-Client", + config: Config{ + MaxVersion: VersionTLS12, + SessionTicketsDisabled: true, + }, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "SessionTicketsDisabled-Server", + config: Config{ + MaxVersion: VersionTLS12, + SessionTicketsDisabled: true, + }, + }) + + // Skip ServerKeyExchange in PSK key exchange if there's no + // identity hint. + tests = append(tests, testCase{ + name: "EmptyPSKHint-Client", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, + PreSharedKey: []byte("secret"), + }, + flags: []string{"-psk", "secret"}, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "EmptyPSKHint-Server", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, + PreSharedKey: []byte("secret"), + }, + flags: []string{"-psk", "secret"}, + }) + + // OCSP stapling tests. + tests = append(tests, testCase{ + testType: clientTest, + name: "OCSPStapling-Client", + config: Config{ + MaxVersion: VersionTLS12, + }, + flags: []string{ + "-enable-ocsp-stapling", + "-expect-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + "-verify-peer", + }, + resumeSession: true, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "OCSPStapling-Server", + config: Config{ + MaxVersion: VersionTLS12, + }, + expectedOCSPResponse: testOCSPResponse, + flags: []string{ + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + resumeSession: true, + }) + tests = append(tests, testCase{ + testType: clientTest, + name: "OCSPStapling-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + }, + flags: []string{ + "-enable-ocsp-stapling", + "-expect-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + "-verify-peer", + }, + resumeSession: true, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "OCSPStapling-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + }, + expectedOCSPResponse: testOCSPResponse, + flags: []string{ + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + resumeSession: true, + }) + + // Certificate verification tests. + for _, vers := range tlsVersions { + if config.protocol == dtls && !vers.hasDTLS { + continue + } + for _, testType := range []testType{clientTest, serverTest} { + suffix := "-Client" + if testType == serverTest { + suffix = "-Server" + } + suffix += "-" + vers.name + + flag := "-verify-peer" + if testType == serverTest { + flag = "-require-any-client-certificate" + } + + tests = append(tests, testCase{ + testType: testType, + name: "CertificateVerificationSucceed" + suffix, + config: Config{ + MaxVersion: vers.version, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{ + flag, + "-expect-verify-result", + }, + resumeSession: true, + }) + tests = append(tests, testCase{ + testType: testType, + name: "CertificateVerificationFail" + suffix, + config: Config{ + MaxVersion: vers.version, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{ + flag, + "-verify-fail", + }, + shouldFail: true, + expectedError: ":CERTIFICATE_VERIFY_FAILED:", + }) + } + + // By default, the client is in a soft fail mode where the peer + // certificate is verified but failures are non-fatal. + tests = append(tests, testCase{ + testType: clientTest, + name: "CertificateVerificationSoftFail-" + vers.name, + config: Config{ + MaxVersion: vers.version, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{ + "-verify-fail", + "-expect-verify-result", + }, + resumeSession: true, + }) + } + + tests = append(tests, testCase{ + name: "ShimSendAlert", + flags: []string{"-send-alert"}, + shimWritesFirst: true, + shouldFail: true, + expectedLocalError: "remote error: decompression failure", + }) + + if config.protocol == tls { + tests = append(tests, testCase{ + name: "Renegotiate-Client", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + }) + + tests = append(tests, testCase{ + name: "SendHalfHelloRequest", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + PackHelloRequestWithFinished: config.packHandshakeFlight, + }, + }, + sendHalfHelloRequest: true, + flags: []string{"-renegotiate-ignore"}, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + + // NPN on client and server; results in post-handshake message. + tests = append(tests, testCase{ + name: "NPN-Client", + config: Config{ + MaxVersion: VersionTLS12, + NextProtos: []string{"foo"}, + }, + flags: []string{"-select-next-proto", "foo"}, + resumeSession: true, + expectedNextProto: "foo", + expectedNextProtoType: npn, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "NPN-Server", + config: Config{ + MaxVersion: VersionTLS12, + NextProtos: []string{"bar"}, + }, + flags: []string{ + "-advertise-npn", "\x03foo\x03bar\x03baz", + "-expect-next-proto", "bar", + }, + resumeSession: true, + expectedNextProto: "bar", + expectedNextProtoType: npn, + }) + + // TODO(davidben): Add tests for when False Start doesn't trigger. + + // Client does False Start and negotiates NPN. + tests = append(tests, testCase{ + name: "FalseStart", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + }, + }, + flags: []string{ + "-false-start", + "-select-next-proto", "foo", + }, + shimWritesFirst: true, + resumeSession: true, + }) + + // Client does False Start and negotiates ALPN. + tests = append(tests, testCase{ + name: "FalseStart-ALPN", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + }, + }, + flags: []string{ + "-false-start", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + resumeSession: true, + }) + + // Client does False Start but doesn't explicitly call + // SSL_connect. + tests = append(tests, testCase{ + name: "FalseStart-Implicit", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + }, + flags: []string{ + "-implicit-handshake", + "-false-start", + "-advertise-alpn", "\x03foo", + }, + }) + + // False Start without session tickets. + tests = append(tests, testCase{ + name: "FalseStart-SessionTicketsDisabled", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + SessionTicketsDisabled: true, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + }, + }, + flags: []string{ + "-false-start", + "-select-next-proto", "foo", + }, + shimWritesFirst: true, + }) + + // Server parses a V2ClientHello. + tests = append(tests, testCase{ + testType: serverTest, + name: "SendV2ClientHello", + config: Config{ + // Choose a cipher suite that does not involve + // elliptic curves, so no extensions are + // involved. + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + Bugs: ProtocolBugs{ + SendV2ClientHello: true, + }, + }, + }) + + // Test Channel ID + for _, ver := range tlsVersions { + if ver.version < VersionTLS10 { + continue + } + // Client sends a Channel ID. + tests = append(tests, testCase{ + name: "ChannelID-Client-" + ver.name, + config: Config{ + MaxVersion: ver.version, + RequestChannelID: true, + }, + flags: []string{"-send-channel-id", path.Join(*resourceDir, channelIDKeyFile)}, + resumeSession: true, + expectChannelID: true, + }) + + // Server accepts a Channel ID. + tests = append(tests, testCase{ + testType: serverTest, + name: "ChannelID-Server-" + ver.name, + config: Config{ + MaxVersion: ver.version, + ChannelID: channelIDKey, + }, + flags: []string{ + "-expect-channel-id", + base64.StdEncoding.EncodeToString(channelIDBytes), + }, + resumeSession: true, + expectChannelID: true, + }) + + tests = append(tests, testCase{ + testType: serverTest, + name: "InvalidChannelIDSignature-" + ver.name, + config: Config{ + MaxVersion: ver.version, + ChannelID: channelIDKey, + Bugs: ProtocolBugs{ + InvalidChannelIDSignature: true, + }, + }, + flags: []string{"-enable-channel-id"}, + shouldFail: true, + expectedError: ":CHANNEL_ID_SIGNATURE_INVALID:", + }) + } + + // Channel ID and NPN at the same time, to ensure their relative + // ordering is correct. + tests = append(tests, testCase{ + name: "ChannelID-NPN-Client", + config: Config{ + MaxVersion: VersionTLS12, + RequestChannelID: true, + NextProtos: []string{"foo"}, + }, + flags: []string{ + "-send-channel-id", path.Join(*resourceDir, channelIDKeyFile), + "-select-next-proto", "foo", + }, + resumeSession: true, + expectChannelID: true, + expectedNextProto: "foo", + expectedNextProtoType: npn, + }) + tests = append(tests, testCase{ + testType: serverTest, + name: "ChannelID-NPN-Server", + config: Config{ + MaxVersion: VersionTLS12, + ChannelID: channelIDKey, + NextProtos: []string{"bar"}, + }, + flags: []string{ + "-expect-channel-id", + base64.StdEncoding.EncodeToString(channelIDBytes), + "-advertise-npn", "\x03foo\x03bar\x03baz", + "-expect-next-proto", "bar", + }, + resumeSession: true, + expectChannelID: true, + expectedNextProto: "bar", + expectedNextProtoType: npn, + }) + + // Bidirectional shutdown with the runner initiating. + tests = append(tests, testCase{ + name: "Shutdown-Runner", + config: Config{ + Bugs: ProtocolBugs{ + ExpectCloseNotify: true, + }, + }, + flags: []string{"-check-close-notify"}, + }) + + // Bidirectional shutdown with the shim initiating. The runner, + // in the meantime, sends garbage before the close_notify which + // the shim must ignore. + tests = append(tests, testCase{ + name: "Shutdown-Shim", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + ExpectCloseNotify: true, + }, + }, + shimShutsDown: true, + sendEmptyRecords: 1, + sendWarningAlerts: 1, + flags: []string{"-check-close-notify"}, + }) + } else { + // TODO(davidben): DTLS 1.3 will want a similar thing for + // HelloRetryRequest. + tests = append(tests, testCase{ + name: "SkipHelloVerifyRequest", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SkipHelloVerifyRequest: true, + }, + }, + }) + } + + for _, test := range tests { + test.protocol = config.protocol + if config.protocol == dtls { + test.name += "-DTLS" + } + if config.async { + test.name += "-Async" + test.flags = append(test.flags, "-async") + } else { + test.name += "-Sync" + } + if config.splitHandshake { + test.name += "-SplitHandshakeRecords" + test.config.Bugs.MaxHandshakeRecordLength = 1 + if config.protocol == dtls { + test.config.Bugs.MaxPacketLength = 256 + test.flags = append(test.flags, "-mtu", "256") + } + } + if config.packHandshakeFlight { + test.name += "-PackHandshakeFlight" + test.config.Bugs.PackHandshakeFlight = true + } + testCases = append(testCases, test) + } +} + +func addDDoSCallbackTests() { + // DDoS callback. + for _, resume := range []bool{false, true} { + suffix := "Resume" + if resume { + suffix = "No" + suffix + } + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Server-DDoS-OK-" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + flags: []string{"-install-ddos-callback"}, + resumeSession: resume, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Server-DDoS-OK-" + suffix + "-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + }, + flags: []string{"-install-ddos-callback"}, + resumeSession: resume, + }) + + failFlag := "-fail-ddos-callback" + if resume { + failFlag = "-fail-second-ddos-callback" + } + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Server-DDoS-Reject-" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + flags: []string{"-install-ddos-callback", failFlag}, + resumeSession: resume, + shouldFail: true, + expectedError: ":CONNECTION_REJECTED:", + expectedLocalError: "remote error: internal error", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Server-DDoS-Reject-" + suffix + "-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + }, + flags: []string{"-install-ddos-callback", failFlag}, + resumeSession: resume, + shouldFail: true, + expectedError: ":CONNECTION_REJECTED:", + expectedLocalError: "remote error: internal error", + }) + } +} + +func addVersionNegotiationTests() { + for i, shimVers := range tlsVersions { + // Assemble flags to disable all newer versions on the shim. + var flags []string + for _, vers := range tlsVersions[i+1:] { + flags = append(flags, vers.flag) + } + + // Test configuring the runner's maximum version. + for _, runnerVers := range tlsVersions { + protocols := []protocol{tls} + if runnerVers.hasDTLS && shimVers.hasDTLS { + protocols = append(protocols, dtls) + } + for _, protocol := range protocols { + expectedVersion := shimVers.version + if runnerVers.version < shimVers.version { + expectedVersion = runnerVers.version + } + + suffix := shimVers.name + "-" + runnerVers.name + if protocol == dtls { + suffix += "-DTLS" + } + + shimVersFlag := strconv.Itoa(int(versionToWire(shimVers.version, protocol == dtls))) + + // Determine the expected initial record-layer versions. + clientVers := shimVers.version + if clientVers > VersionTLS10 { + clientVers = VersionTLS10 + } + clientVers = versionToWire(clientVers, protocol == dtls) + serverVers := expectedVersion + if expectedVersion >= VersionTLS13 { + serverVers = VersionTLS10 + } + serverVers = versionToWire(serverVers, protocol == dtls) + + testCases = append(testCases, testCase{ + protocol: protocol, + testType: clientTest, + name: "VersionNegotiation-Client-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + ExpectInitialRecordVersion: clientVers, + }, + }, + flags: flags, + expectedVersion: expectedVersion, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: clientTest, + name: "VersionNegotiation-Client2-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + ExpectInitialRecordVersion: clientVers, + }, + }, + flags: []string{"-max-version", shimVersFlag}, + expectedVersion: expectedVersion, + }) + + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "VersionNegotiation-Server-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + ExpectInitialRecordVersion: serverVers, + }, + }, + flags: flags, + expectedVersion: expectedVersion, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "VersionNegotiation-Server2-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + ExpectInitialRecordVersion: serverVers, + }, + }, + flags: []string{"-max-version", shimVersFlag}, + expectedVersion: expectedVersion, + }) + } + } + } + + // Test the version extension at all versions. + for _, vers := range tlsVersions { + protocols := []protocol{tls} + if vers.hasDTLS { + protocols = append(protocols, dtls) + } + for _, protocol := range protocols { + suffix := vers.name + if protocol == dtls { + suffix += "-DTLS" + } + + wireVersion := versionToWire(vers.version, protocol == dtls) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "VersionNegotiationExtension-" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{0x1111, wireVersion, 0x2222}, + }, + }, + expectedVersion: vers.version, + }) + } + + } + + // If all versions are unknown, negotiation fails. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "NoSupportedVersions", + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{0x1111}, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "NoSupportedVersions-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{0x1111}, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ClientHelloVersionTooHigh", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendClientVersion: 0x0304, + OmitSupportedVersions: true, + }, + }, + expectedVersion: VersionTLS12, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ConflictingVersionNegotiation", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: VersionTLS12, + SendSupportedVersions: []uint16{VersionTLS11}, + }, + }, + // The extension takes precedence over the ClientHello version. + expectedVersion: VersionTLS11, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ConflictingVersionNegotiation-2", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: VersionTLS11, + SendSupportedVersions: []uint16{VersionTLS12}, + }, + }, + // The extension takes precedence over the ClientHello version. + expectedVersion: VersionTLS12, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "RejectFinalTLS13", + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{VersionTLS13, VersionTLS12}, + }, + }, + // We currently implement a draft TLS 1.3 version. Ensure that + // the true TLS 1.3 value is ignored for now. + expectedVersion: VersionTLS12, + }) + + // Test that the maximum version is selected regardless of the + // client-sent order. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "IgnoreClientVersionOrder", + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{VersionTLS12, tls13DraftVersion}, + }, + }, + expectedVersion: VersionTLS13, + }) + + // Test for version tolerance. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "MinorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x03ff, + OmitSupportedVersions: true, + }, + }, + expectedVersion: VersionTLS12, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "MajorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0400, + OmitSupportedVersions: true, + }, + }, + // TLS 1.3 must be negotiated with the supported_versions + // extension, not ClientHello.version. + expectedVersion: VersionTLS12, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "VersionTolerance-TLS13", + config: Config{ + Bugs: ProtocolBugs{ + // Although TLS 1.3 does not use + // ClientHello.version, it still tolerates high + // values there. + SendClientVersion: 0x0400, + }, + }, + expectedVersion: VersionTLS13, + }) + + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "MinorVersionTolerance-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0xfe00, + OmitSupportedVersions: true, + }, + }, + expectedVersion: VersionTLS12, + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "MajorVersionTolerance-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0xfdff, + OmitSupportedVersions: true, + }, + }, + expectedVersion: VersionTLS12, + }) + + // Test that versions below 3.0 are rejected. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "VersionTooLow", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0200, + OmitSupportedVersions: true, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "VersionTooLow-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0xffff, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) + + testCases = append(testCases, testCase{ + name: "ServerBogusVersion", + config: Config{ + Bugs: ProtocolBugs{ + SendServerHelloVersion: 0x1234, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }) + + // Test TLS 1.3's downgrade signal. + testCases = append(testCases, testCase{ + name: "Downgrade-TLS12-Client", + config: Config{ + Bugs: ProtocolBugs{ + NegotiateVersion: VersionTLS12, + }, + }, + expectedVersion: VersionTLS12, + // TODO(davidben): This test should fail once TLS 1.3 is final + // and the fallback signal restored. + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Downgrade-TLS12-Server", + config: Config{ + Bugs: ProtocolBugs{ + SendSupportedVersions: []uint16{VersionTLS12}, + }, + }, + expectedVersion: VersionTLS12, + // TODO(davidben): This test should fail once TLS 1.3 is final + // and the fallback signal restored. + }) +} + +func addMinimumVersionTests() { + for i, shimVers := range tlsVersions { + // Assemble flags to disable all older versions on the shim. + var flags []string + for _, vers := range tlsVersions[:i] { + flags = append(flags, vers.flag) + } + + for _, runnerVers := range tlsVersions { + protocols := []protocol{tls} + if runnerVers.hasDTLS && shimVers.hasDTLS { + protocols = append(protocols, dtls) + } + for _, protocol := range protocols { + suffix := shimVers.name + "-" + runnerVers.name + if protocol == dtls { + suffix += "-DTLS" + } + shimVersFlag := strconv.Itoa(int(versionToWire(shimVers.version, protocol == dtls))) + + var expectedVersion uint16 + var shouldFail bool + var expectedError, expectedLocalError string + if runnerVers.version >= shimVers.version { + expectedVersion = runnerVers.version + } else { + shouldFail = true + expectedError = ":UNSUPPORTED_PROTOCOL:" + expectedLocalError = "remote error: protocol version not supported" + } + + testCases = append(testCases, testCase{ + protocol: protocol, + testType: clientTest, + name: "MinimumVersion-Client-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + // Ensure the server does not decline to + // select a version (versions extension) or + // cipher (some ciphers depend on versions). + NegotiateVersion: runnerVers.version, + IgnorePeerCipherPreferences: shouldFail, + }, + }, + flags: flags, + expectedVersion: expectedVersion, + shouldFail: shouldFail, + expectedError: expectedError, + expectedLocalError: expectedLocalError, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: clientTest, + name: "MinimumVersion-Client2-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + // Ensure the server does not decline to + // select a version (versions extension) or + // cipher (some ciphers depend on versions). + NegotiateVersion: runnerVers.version, + IgnorePeerCipherPreferences: shouldFail, + }, + }, + flags: []string{"-min-version", shimVersFlag}, + expectedVersion: expectedVersion, + shouldFail: shouldFail, + expectedError: expectedError, + expectedLocalError: expectedLocalError, + }) + + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "MinimumVersion-Server-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + }, + flags: flags, + expectedVersion: expectedVersion, + shouldFail: shouldFail, + expectedError: expectedError, + expectedLocalError: expectedLocalError, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "MinimumVersion-Server2-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + }, + flags: []string{"-min-version", shimVersFlag}, + expectedVersion: expectedVersion, + shouldFail: shouldFail, + expectedError: expectedError, + expectedLocalError: expectedLocalError, + }) + } + } + } +} + +func addExtensionTests() { + // TODO(davidben): Extensions, where applicable, all move their server + // halves to EncryptedExtensions in TLS 1.3. Duplicate each of these + // tests for both. Also test interaction with 0-RTT when implemented. + + // Repeat extensions tests all versions except SSL 3.0. + for _, ver := range tlsVersions { + if ver.version == VersionSSL30 { + continue + } + + // Test that duplicate extensions are rejected. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "DuplicateExtensionClient-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + DuplicateExtension: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: error decoding message", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "DuplicateExtensionServer-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + DuplicateExtension: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: error decoding message", + }) + + // Test SNI. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ServerNameExtensionClient-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + ExpectServerName: "example.com", + }, + }, + flags: []string{"-host-name", "example.com"}, + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ServerNameExtensionClientMismatch-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + ExpectServerName: "mismatch.com", + }, + }, + flags: []string{"-host-name", "example.com"}, + shouldFail: true, + expectedLocalError: "tls: unexpected server name", + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ServerNameExtensionClientMissing-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + ExpectServerName: "missing.com", + }, + }, + shouldFail: true, + expectedLocalError: "tls: unexpected server name", + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TolerateServerNameAck-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SendServerNameAck: true, + }, + }, + flags: []string{"-host-name", "example.com"}, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "UnsolicitedServerNameAck-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SendServerNameAck: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + expectedLocalError: "remote error: unsupported extension", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerNameExtensionServer-" + ver.name, + config: Config{ + MaxVersion: ver.version, + ServerName: "example.com", + }, + flags: []string{"-expect-server-name", "example.com"}, + resumeSession: true, + }) + + // Test ALPN. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ALPNClient-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{"foo"}, + }, + flags: []string{ + "-advertise-alpn", "\x03foo\x03bar\x03baz", + "-expect-alpn", "foo", + }, + expectedNextProto: "foo", + expectedNextProtoType: alpn, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ALPNClient-Mismatch-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SendALPN: "baz", + }, + }, + flags: []string{ + "-advertise-alpn", "\x03foo\x03bar", + }, + shouldFail: true, + expectedError: ":INVALID_ALPN_PROTOCOL:", + expectedLocalError: "remote error: illegal parameter", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{"foo", "bar", "baz"}, + }, + flags: []string{ + "-expect-advertised-alpn", "\x03foo\x03bar\x03baz", + "-select-alpn", "foo", + }, + expectedNextProto: "foo", + expectedNextProtoType: alpn, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer-Decline-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{"foo", "bar", "baz"}, + }, + flags: []string{"-decline-alpn"}, + expectNoNextProto: true, + resumeSession: true, + }) + + // Test ALPN in async mode as well to ensure that extensions callbacks are only + // called once. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer-Async-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{"foo", "bar", "baz"}, + // Prior to TLS 1.3, exercise the asynchronous session callback. + SessionTicketsDisabled: ver.version < VersionTLS13, + }, + flags: []string{ + "-expect-advertised-alpn", "\x03foo\x03bar\x03baz", + "-select-alpn", "foo", + "-async", + }, + expectedNextProto: "foo", + expectedNextProtoType: alpn, + resumeSession: true, + }) + + var emptyString string + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ALPNClient-EmptyProtocolName-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{""}, + Bugs: ProtocolBugs{ + // A server returning an empty ALPN protocol + // should be rejected. + ALPNProtocol: &emptyString, + }, + }, + flags: []string{ + "-advertise-alpn", "\x03foo", + }, + shouldFail: true, + expectedError: ":PARSE_TLSEXT:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer-EmptyProtocolName-" + ver.name, + config: Config{ + MaxVersion: ver.version, + // A ClientHello containing an empty ALPN protocol + // should be rejected. + NextProtos: []string{"foo", "", "baz"}, + }, + flags: []string{ + "-select-alpn", "foo", + }, + shouldFail: true, + expectedError: ":PARSE_TLSEXT:", + }) + + // Test NPN and the interaction with ALPN. + if ver.version < VersionTLS13 { + // Test that the server prefers ALPN over NPN. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer-Preferred-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{"foo", "bar", "baz"}, + }, + flags: []string{ + "-expect-advertised-alpn", "\x03foo\x03bar\x03baz", + "-select-alpn", "foo", + "-advertise-npn", "\x03foo\x03bar\x03baz", + }, + expectedNextProto: "foo", + expectedNextProtoType: alpn, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer-Preferred-Swapped-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{"foo", "bar", "baz"}, + Bugs: ProtocolBugs{ + SwapNPNAndALPN: true, + }, + }, + flags: []string{ + "-expect-advertised-alpn", "\x03foo\x03bar\x03baz", + "-select-alpn", "foo", + "-advertise-npn", "\x03foo\x03bar\x03baz", + }, + expectedNextProto: "foo", + expectedNextProtoType: alpn, + resumeSession: true, + }) + + // Test that negotiating both NPN and ALPN is forbidden. + testCases = append(testCases, testCase{ + name: "NegotiateALPNAndNPN-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{"foo", "bar", "baz"}, + Bugs: ProtocolBugs{ + NegotiateALPNAndNPN: true, + }, + }, + flags: []string{ + "-advertise-alpn", "\x03foo", + "-select-next-proto", "foo", + }, + shouldFail: true, + expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:", + }) + testCases = append(testCases, testCase{ + name: "NegotiateALPNAndNPN-Swapped-" + ver.name, + config: Config{ + MaxVersion: ver.version, + NextProtos: []string{"foo", "bar", "baz"}, + Bugs: ProtocolBugs{ + NegotiateALPNAndNPN: true, + SwapNPNAndALPN: true, + }, + }, + flags: []string{ + "-advertise-alpn", "\x03foo", + "-select-next-proto", "foo", + }, + shouldFail: true, + expectedError: ":NEGOTIATED_BOTH_NPN_AND_ALPN:", + }) + } + + // Test ticket behavior. + + // Resume with a corrupt ticket. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CorruptTicket-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + FilterTicket: func(in []byte) ([]byte, error) { + in[len(in)-1] ^= 1 + return in, nil + }, + }, + }, + resumeSession: true, + expectResumeRejected: true, + }) + // Test the ticket callback, with and without renewal. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketCallback-" + ver.name, + config: Config{ + MaxVersion: ver.version, + }, + resumeSession: true, + flags: []string{"-use-ticket-callback"}, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketCallback-Renew-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + ExpectNewTicket: true, + }, + }, + flags: []string{"-use-ticket-callback", "-renew-ticket"}, + resumeSession: true, + }) + + // Test that the ticket callback is only called once when everything before + // it in the ClientHello is asynchronous. This corrupts the ticket so + // certificate selection callbacks run. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketCallback-SingleCall-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + FilterTicket: func(in []byte) ([]byte, error) { + in[len(in)-1] ^= 1 + return in, nil + }, + }, + }, + resumeSession: true, + expectResumeRejected: true, + flags: []string{ + "-use-ticket-callback", + "-async", + }, + }) + + // Resume with various lengths of ticket session id. + if ver.version < VersionTLS13 { + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketSessionIDLength-0-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + EmptyTicketSessionID: true, + }, + }, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketSessionIDLength-16-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + TicketSessionIDLength: 16, + }, + }, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketSessionIDLength-32-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + TicketSessionIDLength: 32, + }, + }, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TicketSessionIDLength-33-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + TicketSessionIDLength: 33, + }, + }, + resumeSession: true, + shouldFail: true, + // The maximum session ID length is 32. + expectedError: ":DECODE_ERROR:", + }) + } + + // Basic DTLS-SRTP tests. Include fake profiles to ensure they + // are ignored. + if ver.hasDTLS { + testCases = append(testCases, testCase{ + protocol: dtls, + name: "SRTP-Client-" + ver.name, + config: Config{ + MaxVersion: ver.version, + SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42}, + }, + flags: []string{ + "-srtp-profiles", + "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32", + }, + expectedSRTPProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80, + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "SRTP-Server-" + ver.name, + config: Config{ + MaxVersion: ver.version, + SRTPProtectionProfiles: []uint16{40, SRTP_AES128_CM_HMAC_SHA1_80, 42}, + }, + flags: []string{ + "-srtp-profiles", + "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32", + }, + expectedSRTPProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80, + }) + // Test that the MKI is ignored. + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "SRTP-Server-IgnoreMKI-" + ver.name, + config: Config{ + MaxVersion: ver.version, + SRTPProtectionProfiles: []uint16{SRTP_AES128_CM_HMAC_SHA1_80}, + Bugs: ProtocolBugs{ + SRTPMasterKeyIdentifer: "bogus", + }, + }, + flags: []string{ + "-srtp-profiles", + "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32", + }, + expectedSRTPProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_80, + }) + // Test that SRTP isn't negotiated on the server if there were + // no matching profiles. + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "SRTP-Server-NoMatch-" + ver.name, + config: Config{ + MaxVersion: ver.version, + SRTPProtectionProfiles: []uint16{100, 101, 102}, + }, + flags: []string{ + "-srtp-profiles", + "SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32", + }, + expectedSRTPProtectionProfile: 0, + }) + // Test that the server returning an invalid SRTP profile is + // flagged as an error by the client. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "SRTP-Client-NoMatch-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SendSRTPProtectionProfile: SRTP_AES128_CM_HMAC_SHA1_32, + }, + }, + flags: []string{ + "-srtp-profiles", + "SRTP_AES128_CM_SHA1_80", + }, + shouldFail: true, + expectedError: ":BAD_SRTP_PROTECTION_PROFILE_LIST:", + }) + } + + // Test SCT list. + testCases = append(testCases, testCase{ + name: "SignedCertificateTimestampList-Client-" + ver.name, + testType: clientTest, + config: Config{ + MaxVersion: ver.version, + }, + flags: []string{ + "-enable-signed-cert-timestamps", + "-expect-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), + }, + resumeSession: true, + }) + + var differentSCTList []byte + differentSCTList = append(differentSCTList, testSCTList...) + differentSCTList[len(differentSCTList)-1] ^= 1 + + // The SCT extension did not specify that it must only be sent on resumption as it + // should have, so test that we tolerate but ignore it. + testCases = append(testCases, testCase{ + name: "SendSCTListOnResume-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SendSCTListOnResume: differentSCTList, + }, + }, + flags: []string{ + "-enable-signed-cert-timestamps", + "-expect-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), + }, + resumeSession: true, + }) + + testCases = append(testCases, testCase{ + name: "SignedCertificateTimestampList-Server-" + ver.name, + testType: serverTest, + config: Config{ + MaxVersion: ver.version, + }, + flags: []string{ + "-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), + }, + expectedSCTList: testSCTList, + resumeSession: true, + }) + + emptySCTListCert := *testCerts[0].cert + emptySCTListCert.SignedCertificateTimestampList = []byte{0, 0} + + // Test empty SCT list. + testCases = append(testCases, testCase{ + name: "SignedCertificateTimestampListEmpty-Client-" + ver.name, + testType: clientTest, + config: Config{ + MaxVersion: ver.version, + Certificates: []Certificate{emptySCTListCert}, + }, + flags: []string{ + "-enable-signed-cert-timestamps", + }, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) + + emptySCTCert := *testCerts[0].cert + emptySCTCert.SignedCertificateTimestampList = []byte{0, 6, 0, 2, 1, 2, 0, 0} + + // Test empty SCT in non-empty list. + testCases = append(testCases, testCase{ + name: "SignedCertificateTimestampListEmptySCT-Client-" + ver.name, + testType: clientTest, + config: Config{ + MaxVersion: ver.version, + Certificates: []Certificate{emptySCTCert}, + }, + flags: []string{ + "-enable-signed-cert-timestamps", + }, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) + + // Test that certificate-related extensions are not sent unsolicited. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "UnsolicitedCertificateExtensions-" + ver.name, + config: Config{ + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + NoOCSPStapling: true, + NoSignedCertificateTimestamps: true, + }, + }, + flags: []string{ + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + "-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), + }, + }) + } + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ClientHelloPadding", + config: Config{ + Bugs: ProtocolBugs{ + RequireClientHelloSize: 512, + }, + }, + // This hostname just needs to be long enough to push the + // ClientHello into F5's danger zone between 256 and 511 bytes + // long. + flags: []string{"-host-name", "01234567890123456789012345678901234567890123456789012345678901234567890123456789.com"}, + }) + + // Extensions should not function in SSL 3.0. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SSLv3Extensions-NoALPN", + config: Config{ + MaxVersion: VersionSSL30, + NextProtos: []string{"foo", "bar", "baz"}, + }, + flags: []string{ + "-select-alpn", "foo", + }, + expectNoNextProto: true, + }) + + // Test session tickets separately as they follow a different codepath. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SSLv3Extensions-NoTickets", + config: Config{ + MaxVersion: VersionSSL30, + Bugs: ProtocolBugs{ + // Historically, session tickets in SSL 3.0 + // failed in different ways depending on whether + // the client supported renegotiation_info. + NoRenegotiationInfo: true, + }, + }, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SSLv3Extensions-NoTickets2", + config: Config{ + MaxVersion: VersionSSL30, + }, + resumeSession: true, + }) + + // But SSL 3.0 does send and process renegotiation_info. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SSLv3Extensions-RenegotiationInfo", + config: Config{ + MaxVersion: VersionSSL30, + Bugs: ProtocolBugs{ + RequireRenegotiationInfo: true, + }, + }, + flags: []string{"-expect-secure-renegotiation"}, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SSLv3Extensions-RenegotiationInfo-SCSV", + config: Config{ + MaxVersion: VersionSSL30, + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + SendRenegotiationSCSV: true, + RequireRenegotiationInfo: true, + }, + }, + flags: []string{"-expect-secure-renegotiation"}, + }) + + // Test that illegal extensions in TLS 1.3 are rejected by the client if + // in ServerHello. + testCases = append(testCases, testCase{ + name: "NPN-Forbidden-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + NegotiateNPNAtAllVersions: true, + }, + }, + flags: []string{"-select-next-proto", "foo"}, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) + testCases = append(testCases, testCase{ + name: "EMS-Forbidden-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + NegotiateEMSAtAllVersions: true, + }, + }, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) + testCases = append(testCases, testCase{ + name: "RenegotiationInfo-Forbidden-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + NegotiateRenegotiationInfoAtAllVersions: true, + }, + }, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) + testCases = append(testCases, testCase{ + name: "Ticket-Forbidden-TLS13", + config: Config{ + MaxVersion: VersionTLS12, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + AdvertiseTicketExtension: true, + }, + }, + resumeSession: true, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) + + // Test that illegal extensions in TLS 1.3 are declined by the server if + // offered in ClientHello. The runner's server will fail if this occurs, + // so we exercise the offering path. (EMS and Renegotiation Info are + // implicit in every test.) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "NPN-Declined-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + NextProtos: []string{"bar"}, + }, + flags: []string{"-advertise-npn", "\x03foo\x03bar\x03baz"}, + }) + + // OpenSSL sends the status_request extension on resumption in TLS 1.2. Test that this is + // tolerated. + testCases = append(testCases, testCase{ + name: "SendOCSPResponseOnResume-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendOCSPResponseOnResume: []byte("bogus"), + }, + }, + flags: []string{ + "-enable-ocsp-stapling", + "-expect-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + resumeSession: true, + }) + + testCases = append(testCases, testCase{ + name: "SendUnsolicitedOCSPOnCertificate-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendExtensionOnCertificate: testOCSPExtension, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + }) + + testCases = append(testCases, testCase{ + name: "SendUnsolicitedSCTOnCertificate-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendExtensionOnCertificate: testSCTExtension, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + }) + + // Test that extensions on client certificates are never accepted. + testCases = append(testCases, testCase{ + name: "SendExtensionOnClientCertificate-TLS13", + testType: serverTest, + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{rsaCertificate}, + Bugs: ProtocolBugs{ + SendExtensionOnCertificate: testOCSPExtension, + }, + }, + flags: []string{ + "-enable-ocsp-stapling", + "-require-any-client-certificate", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + }) + + testCases = append(testCases, testCase{ + name: "SendUnknownExtensionOnCertificate-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendExtensionOnCertificate: []byte{0x00, 0x7f, 0, 0}, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + }) + + var differentSCTList []byte + differentSCTList = append(differentSCTList, testSCTList...) + differentSCTList[len(differentSCTList)-1] ^= 1 + + // Test that extensions on intermediates are allowed but ignored. + testCases = append(testCases, testCase{ + name: "IgnoreExtensionsOnIntermediates-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{rsaChainCertificate}, + Bugs: ProtocolBugs{ + // Send different values on the intermediate. This tests + // the intermediate's extensions do not override the + // leaf's. + SendOCSPOnIntermediates: []byte{1, 3, 3, 7}, + SendSCTOnIntermediates: differentSCTList, + }, + }, + flags: []string{ + "-enable-ocsp-stapling", + "-expect-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + "-enable-signed-cert-timestamps", + "-expect-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), + }, + resumeSession: true, + }) + + // Test that extensions are not sent on intermediates when configured + // only for a leaf. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SendNoExtensionsOnIntermediate-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExpectNoExtensionsOnIntermediate: true, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile), + "-key-file", path.Join(*resourceDir, rsaChainKeyFile), + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + "-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), + }, + }) + + // Test that extensions are not sent on client certificates. + testCases = append(testCases, testCase{ + name: "SendNoClientCertificateExtensions-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + "-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), + }, + }) + + testCases = append(testCases, testCase{ + name: "SendDuplicateExtensionsOnCerts-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendDuplicateCertExtensions: true, + }, + }, + flags: []string{ + "-enable-ocsp-stapling", + "-enable-signed-cert-timestamps", + }, + resumeSession: true, + shouldFail: true, + expectedError: ":DUPLICATE_EXTENSION:", + }) + + testCases = append(testCases, testCase{ + name: "SignedCertificateTimestampListInvalid-Server", + testType: serverTest, + flags: []string{ + "-signed-cert-timestamps", + base64.StdEncoding.EncodeToString([]byte{0, 0}), + }, + shouldFail: true, + expectedError: ":INVALID_SCT_LIST:", + }) +} + +func addResumptionVersionTests() { + for _, sessionVers := range tlsVersions { + for _, resumeVers := range tlsVersions { + // SSL 3.0 does not have tickets and TLS 1.3 does not + // have session IDs, so skip their cross-resumption + // tests. + if (sessionVers.version >= VersionTLS13 && resumeVers.version == VersionSSL30) || + (resumeVers.version >= VersionTLS13 && sessionVers.version == VersionSSL30) { + continue + } + + protocols := []protocol{tls} + if sessionVers.hasDTLS && resumeVers.hasDTLS { + protocols = append(protocols, dtls) + } + for _, protocol := range protocols { + suffix := "-" + sessionVers.name + "-" + resumeVers.name + if protocol == dtls { + suffix += "-DTLS" + } + + if sessionVers.version == resumeVers.version { + testCases = append(testCases, testCase{ + protocol: protocol, + name: "Resume-Client" + suffix, + resumeSession: true, + config: Config{ + MaxVersion: sessionVers.version, + Bugs: ProtocolBugs{ + ExpectNoTLS12Session: sessionVers.version >= VersionTLS13, + ExpectNoTLS13PSK: sessionVers.version < VersionTLS13, + }, + }, + expectedVersion: sessionVers.version, + expectedResumeVersion: resumeVers.version, + }) + } else { + error := ":OLD_SESSION_VERSION_NOT_RETURNED:" + + // Offering a TLS 1.3 session sends an empty session ID, so + // there is no way to convince a non-lookahead client the + // session was resumed. It will appear to the client that a + // stray ChangeCipherSpec was sent. + if resumeVers.version < VersionTLS13 && sessionVers.version >= VersionTLS13 { + error = ":UNEXPECTED_RECORD:" + } + + testCases = append(testCases, testCase{ + protocol: protocol, + name: "Resume-Client-Mismatch" + suffix, + resumeSession: true, + config: Config{ + MaxVersion: sessionVers.version, + }, + expectedVersion: sessionVers.version, + resumeConfig: &Config{ + MaxVersion: resumeVers.version, + Bugs: ProtocolBugs{ + AcceptAnySession: true, + }, + }, + expectedResumeVersion: resumeVers.version, + shouldFail: true, + expectedError: error, + }) + } + + testCases = append(testCases, testCase{ + protocol: protocol, + name: "Resume-Client-NoResume" + suffix, + resumeSession: true, + config: Config{ + MaxVersion: sessionVers.version, + }, + expectedVersion: sessionVers.version, + resumeConfig: &Config{ + MaxVersion: resumeVers.version, + }, + newSessionsOnResume: true, + expectResumeRejected: true, + expectedResumeVersion: resumeVers.version, + }) + + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "Resume-Server" + suffix, + resumeSession: true, + config: Config{ + MaxVersion: sessionVers.version, + }, + expectedVersion: sessionVers.version, + expectResumeRejected: sessionVers.version != resumeVers.version, + resumeConfig: &Config{ + MaxVersion: resumeVers.version, + Bugs: ProtocolBugs{ + SendBothTickets: true, + }, + }, + expectedResumeVersion: resumeVers.version, + }) + } + } + } + + // Make sure shim ticket mutations are functional. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ShimTicketRewritable", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + FilterTicket: func(in []byte) ([]byte, error) { + in, err := SetShimTicketVersion(in, VersionTLS12) + if err != nil { + return nil, err + } + return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) + }, + }, + }, + flags: []string{ + "-ticket-key", + base64.StdEncoding.EncodeToString(TestShimTicketKey), + }, + }) + + // Resumptions are declined if the version does not match. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-DeclineCrossVersion", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + ExpectNewTicket: true, + FilterTicket: func(in []byte) ([]byte, error) { + return SetShimTicketVersion(in, VersionTLS13) + }, + }, + }, + flags: []string{ + "-ticket-key", + base64.StdEncoding.EncodeToString(TestShimTicketKey), + }, + expectResumeRejected: true, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-DeclineCrossVersion-TLS13", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + FilterTicket: func(in []byte) ([]byte, error) { + return SetShimTicketVersion(in, VersionTLS12) + }, + }, + }, + flags: []string{ + "-ticket-key", + base64.StdEncoding.EncodeToString(TestShimTicketKey), + }, + expectResumeRejected: true, + }) + + // Resumptions are declined if the cipher is invalid or disabled. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-DeclineBadCipher", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + ExpectNewTicket: true, + FilterTicket: func(in []byte) ([]byte, error) { + return SetShimTicketCipherSuite(in, TLS_AES_128_GCM_SHA256) + }, + }, + }, + flags: []string{ + "-ticket-key", + base64.StdEncoding.EncodeToString(TestShimTicketKey), + }, + expectResumeRejected: true, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-DeclineBadCipher-2", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + ExpectNewTicket: true, + FilterTicket: func(in []byte) ([]byte, error) { + return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) + }, + }, + }, + flags: []string{ + "-cipher", "AES128", + "-ticket-key", + base64.StdEncoding.EncodeToString(TestShimTicketKey), + }, + expectResumeRejected: true, + }) + + // Sessions are not resumed if they do not use the preferred cipher. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-CipherNotPreferred", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + ExpectNewTicket: true, + FilterTicket: func(in []byte) ([]byte, error) { + return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA) + }, + }, + }, + flags: []string{ + "-ticket-key", + base64.StdEncoding.EncodeToString(TestShimTicketKey), + }, + shouldFail: false, + expectResumeRejected: true, + }) + + // TLS 1.3 allows sessions to be resumed at a different cipher if their + // PRF hashes match, but BoringSSL will always decline such resumptions. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-CipherNotPreferred-TLS13", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256, TLS_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + FilterTicket: func(in []byte) ([]byte, error) { + // If the client (runner) offers ChaCha20-Poly1305 first, the + // server (shim) always prefers it. Switch it to AES-GCM. + return SetShimTicketCipherSuite(in, TLS_AES_128_GCM_SHA256) + }, + }, + }, + flags: []string{ + "-ticket-key", + base64.StdEncoding.EncodeToString(TestShimTicketKey), + }, + shouldFail: false, + expectResumeRejected: true, + }) + + // Sessions may not be resumed if they contain another version's cipher. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-DeclineBadCipher-TLS13", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + FilterTicket: func(in []byte) ([]byte, error) { + return SetShimTicketCipherSuite(in, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) + }, + }, + }, + flags: []string{ + "-ticket-key", + base64.StdEncoding.EncodeToString(TestShimTicketKey), + }, + expectResumeRejected: true, + }) + + // If the client does not offer the cipher from the session, decline to + // resume. Clients are forbidden from doing this, but BoringSSL selects + // the cipher first, so we only decline. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-UnofferedCipher", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, + Bugs: ProtocolBugs{ + SendCipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + }, + }, + expectResumeRejected: true, + }) + + // In TLS 1.3, clients may advertise a cipher list which does not + // include the selected cipher. Test that we tolerate this. Servers may + // resume at another cipher if the PRF matches, but BoringSSL will + // always decline. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-UnofferedCipher-TLS13", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256}, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256}, + Bugs: ProtocolBugs{ + SendCipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, + }, + }, + expectResumeRejected: true, + }) + + // Sessions may not be resumed at a different cipher. + testCases = append(testCases, testCase{ + name: "Resume-Client-CipherMismatch", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_RSA_WITH_AES_128_CBC_SHA, + }, + }, + shouldFail: true, + expectedError: ":OLD_SESSION_CIPHER_NOT_RETURNED:", + }) + + // Session resumption in TLS 1.3 may change the cipher suite if the PRF + // matches. + testCases = append(testCases, testCase{ + name: "Resume-Client-CipherMismatch-TLS13", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256}, + }, + }) + + // Session resumption in TLS 1.3 is forbidden if the PRF does not match. + testCases = append(testCases, testCase{ + name: "Resume-Client-PRFMismatch-TLS13", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_AES_256_GCM_SHA384, + }, + }, + shouldFail: true, + expectedError: ":OLD_SESSION_PRF_HASH_MISMATCH:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-BinderWrongLength", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendShortPSKBinder: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: error decrypting message", + expectedError: ":DIGEST_CHECK_FAILED:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-NoPSKBinder", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendNoPSKBinder: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: error decoding message", + expectedError: ":DECODE_ERROR:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-ExtraPSKBinder", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendExtraPSKBinder: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: illegal parameter", + expectedError: ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-ExtraIdentityNoBinder", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExtraPSKIdentity: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: illegal parameter", + expectedError: ":PSK_IDENTITY_BINDER_COUNT_MISMATCH:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-InvalidPSKBinder", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendInvalidPSKBinder: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: error decrypting message", + expectedError: ":DIGEST_CHECK_FAILED:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Resume-Server-PSKBinderFirstExtension", + resumeSession: true, + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + PSKBinderFirst: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: illegal parameter", + expectedError: ":PRE_SHARED_KEY_MUST_BE_LAST:", + }) +} + +func addRenegotiationTests() { + // Servers cannot renegotiate. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-Forbidden", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 1, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + expectedLocalError: "remote error: no renegotiation", + }) + // The server shouldn't echo the renegotiation extension unless + // requested by the client. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-NoExt", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + RequireRenegotiationInfo: true, + }, + }, + shouldFail: true, + expectedLocalError: "renegotiation extension missing", + }) + // The renegotiation SCSV should be sufficient for the server to echo + // the extension. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-NoExt-SCSV", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + SendRenegotiationSCSV: true, + RequireRenegotiationInfo: true, + }, + }, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + FailIfResumeOnRenego: true, + }, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + "-expect-secure-renegotiation", + }, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-EmptyExt", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + EmptyRenegotiationInfo: true, + }, + }, + flags: []string{"-renegotiate-freely"}, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-BadExt", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadRenegotiationInfo: true, + }, + }, + flags: []string{"-renegotiate-freely"}, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Downgrade", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoRenegotiationInfoAfterInitial: true, + }, + }, + flags: []string{"-renegotiate-freely"}, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Upgrade", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoRenegotiationInfoInInitial: true, + }, + }, + flags: []string{"-renegotiate-freely"}, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-NoExt-Allowed", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + }, + }, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + "-expect-no-secure-renegotiation", + }, + }) + + // Test that the server may switch ciphers on renegotiation without + // problems. + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-SwitchCiphers", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + }, + renegotiateCiphers: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-SwitchCiphers2", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + }, + renegotiateCiphers: []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + }) + + // Test that the server may not switch versions on renegotiation. + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-SwitchVersion", + config: Config{ + MaxVersion: VersionTLS12, + // Pick a cipher which exists at both versions. + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + NegotiateVersionOnRenego: VersionTLS11, + // Avoid failing early at the record layer. + SendRecordVersion: VersionTLS12, + }, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + shouldFail: true, + expectedError: ":WRONG_SSL_VERSION:", + }) + + testCases = append(testCases, testCase{ + name: "Renegotiate-SameClientVersion", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS10, + Bugs: ProtocolBugs{ + RequireSameRenegoClientVersion: true, + }, + }, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-FalseStart", + renegotiate: 1, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + }, + flags: []string{ + "-false-start", + "-select-next-proto", "foo", + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + shimWritesFirst: true, + }) + + // Client-side renegotiation controls. + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Forbidden-1", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 1, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + expectedLocalError: "remote error: no renegotiation", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Once-1", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-once", + "-expect-total-renegotiations", "1", + }, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Freely-1", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Once-2", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 2, + flags: []string{"-renegotiate-once"}, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + expectedLocalError: "remote error: no renegotiation", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Freely-2", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 2, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "2", + }, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-NoIgnore", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendHelloRequestBeforeEveryAppDataRecord: true, + }, + }, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Ignore", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendHelloRequestBeforeEveryAppDataRecord: true, + }, + }, + flags: []string{ + "-renegotiate-ignore", + "-expect-total-renegotiations", "0", + }, + }) + + // Renegotiation is not allowed at SSL 3.0. + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-SSL3", + config: Config{ + MaxVersion: VersionSSL30, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + expectedLocalError: "remote error: no renegotiation", + }) + + // Renegotiation is not allowed when there is an unfinished write. + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-UnfinishedWrite", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 1, + flags: []string{ + "-async", + "-renegotiate-freely", + "-read-with-unfinished-write", + }, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + // We do not successfully send the no_renegotiation alert in + // this case. https://crbug.com/boringssl/130 + }) + + // We reject stray HelloRequests during the handshake in TLS 1.2. + testCases = append(testCases, testCase{ + name: "StrayHelloRequest", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendHelloRequestBeforeEveryHandshakeMessage: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }) + testCases = append(testCases, testCase{ + name: "StrayHelloRequest-Packed", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + PackHandshakeFlight: true, + SendHelloRequestBeforeEveryHandshakeMessage: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }) + + // Test renegotiation works if HelloRequest and server Finished come in + // the same record. + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-Packed", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + PackHandshakeFlight: true, + PackHelloRequestWithFinished: true, + }, + }, + renegotiate: 1, + flags: []string{ + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + }) + + // Renegotiation is forbidden in TLS 1.3. + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendHelloRequestBeforeEveryAppDataRecord: true, + }, + }, + flags: []string{ + "-renegotiate-freely", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }) + + // Stray HelloRequests during the handshake are forbidden in TLS 1.3. + testCases = append(testCases, testCase{ + name: "StrayHelloRequest-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendHelloRequestBeforeEveryHandshakeMessage: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }) + + // The renegotiation_info extension is not sent in TLS 1.3, but TLS 1.3 + // always reads as supporting it, regardless of whether it was + // negotiated. + testCases = append(testCases, testCase{ + name: "AlwaysReportRenegotiationInfo-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + }, + }, + flags: []string{ + "-expect-secure-renegotiation", + }, + }) + + // Certificates may not change on renegotiation. + testCases = append(testCases, testCase{ + name: "Renegotiation-CertificateChange", + config: Config{ + MaxVersion: VersionTLS12, + Certificates: []Certificate{rsaCertificate}, + Bugs: ProtocolBugs{ + RenegotiationCertificate: &rsaChainCertificate, + }, + }, + renegotiate: 1, + flags: []string{"-renegotiate-freely"}, + shouldFail: true, + expectedError: ":SERVER_CERT_CHANGED:", + }) + testCases = append(testCases, testCase{ + name: "Renegotiation-CertificateChange-2", + config: Config{ + MaxVersion: VersionTLS12, + Certificates: []Certificate{rsaCertificate}, + Bugs: ProtocolBugs{ + RenegotiationCertificate: &rsa1024Certificate, + }, + }, + renegotiate: 1, + flags: []string{"-renegotiate-freely"}, + shouldFail: true, + expectedError: ":SERVER_CERT_CHANGED:", + }) +} + +func addDTLSReplayTests() { + // Test that sequence number replays are detected. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Replay", + messageCount: 200, + replayWrites: true, + }) + + // Test the incoming sequence number skipping by values larger + // than the retransmit window. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Replay-LargeGaps", + config: Config{ + Bugs: ProtocolBugs{ + SequenceNumberMapping: func(in uint64) uint64 { + return in * 127 + }, + }, + }, + messageCount: 200, + replayWrites: true, + }) + + // Test the incoming sequence number changing non-monotonically. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Replay-NonMonotonic", + config: Config{ + Bugs: ProtocolBugs{ + SequenceNumberMapping: func(in uint64) uint64 { + return in ^ 31 + }, + }, + }, + messageCount: 200, + replayWrites: true, + }) +} + +var testSignatureAlgorithms = []struct { + name string + id signatureAlgorithm + cert testCert +}{ + {"RSA-PKCS1-SHA1", signatureRSAPKCS1WithSHA1, testCertRSA}, + {"RSA-PKCS1-SHA256", signatureRSAPKCS1WithSHA256, testCertRSA}, + {"RSA-PKCS1-SHA384", signatureRSAPKCS1WithSHA384, testCertRSA}, + {"RSA-PKCS1-SHA512", signatureRSAPKCS1WithSHA512, testCertRSA}, + {"ECDSA-SHA1", signatureECDSAWithSHA1, testCertECDSAP256}, + {"ECDSA-P256-SHA256", signatureECDSAWithP256AndSHA256, testCertECDSAP256}, + {"ECDSA-P384-SHA384", signatureECDSAWithP384AndSHA384, testCertECDSAP384}, + {"ECDSA-P521-SHA512", signatureECDSAWithP521AndSHA512, testCertECDSAP521}, + {"RSA-PSS-SHA256", signatureRSAPSSWithSHA256, testCertRSA}, + {"RSA-PSS-SHA384", signatureRSAPSSWithSHA384, testCertRSA}, + {"RSA-PSS-SHA512", signatureRSAPSSWithSHA512, testCertRSA}, + // Tests for key types prior to TLS 1.2. + {"RSA", 0, testCertRSA}, + {"ECDSA", 0, testCertECDSAP256}, +} + +const fakeSigAlg1 signatureAlgorithm = 0x2a01 +const fakeSigAlg2 signatureAlgorithm = 0xff01 + +func addSignatureAlgorithmTests() { + // Not all ciphers involve a signature. Advertise a list which gives all + // versions a signing cipher. + signingCiphers := []uint16{ + TLS_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + } + + var allAlgorithms []signatureAlgorithm + for _, alg := range testSignatureAlgorithms { + if alg.id != 0 { + allAlgorithms = append(allAlgorithms, alg.id) + } + } + + // Make sure each signature algorithm works. Include some fake values in + // the list and ensure they're ignored. + for _, alg := range testSignatureAlgorithms { + for _, ver := range tlsVersions { + if (ver.version < VersionTLS12) != (alg.id == 0) { + continue + } + + // TODO(davidben): Support ECDSA in SSL 3.0 in Go for testing + // or remove it in C. + if ver.version == VersionSSL30 && alg.cert != testCertRSA { + continue + } + + var shouldSignFail, shouldVerifyFail bool + // ecdsa_sha1 does not exist in TLS 1.3. + if ver.version >= VersionTLS13 && alg.id == signatureECDSAWithSHA1 { + shouldSignFail = true + shouldVerifyFail = true + } + // RSA-PKCS1 does not exist in TLS 1.3. + if ver.version == VersionTLS13 && hasComponent(alg.name, "PKCS1") { + shouldSignFail = true + shouldVerifyFail = true + } + + // BoringSSL will sign SHA-1 and SHA-512 with ECDSA but not accept them. + if alg.id == signatureECDSAWithSHA1 || alg.id == signatureECDSAWithP521AndSHA512 { + shouldVerifyFail = true + } + + var signError, verifyError string + if shouldSignFail { + signError = ":NO_COMMON_SIGNATURE_ALGORITHMS:" + } + if shouldVerifyFail { + verifyError = ":WRONG_SIGNATURE_TYPE:" + } + + suffix := "-" + alg.name + "-" + ver.name + + testCases = append(testCases, testCase{ + name: "ClientAuth-Sign" + suffix, + config: Config{ + MaxVersion: ver.version, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + fakeSigAlg1, + alg.id, + fakeSigAlg2, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)), + "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)), + "-enable-all-curves", + }, + shouldFail: shouldSignFail, + expectedError: signError, + expectedPeerSignatureAlgorithm: alg.id, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ClientAuth-Verify" + suffix, + config: Config{ + MaxVersion: ver.version, + Certificates: []Certificate{getRunnerCertificate(alg.cert)}, + SignSignatureAlgorithms: []signatureAlgorithm{ + alg.id, + }, + Bugs: ProtocolBugs{ + SkipECDSACurveCheck: shouldVerifyFail, + IgnoreSignatureVersionChecks: shouldVerifyFail, + // Some signature algorithms may not be advertised. + IgnorePeerSignatureAlgorithmPreferences: shouldVerifyFail, + }, + }, + flags: []string{ + "-require-any-client-certificate", + "-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id)), + "-enable-all-curves", + }, + // Resume the session to assert the peer signature + // algorithm is reported on both handshakes. + resumeSession: !shouldVerifyFail, + shouldFail: shouldVerifyFail, + expectedError: verifyError, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerAuth-Sign" + suffix, + config: Config{ + MaxVersion: ver.version, + CipherSuites: signingCiphers, + VerifySignatureAlgorithms: []signatureAlgorithm{ + fakeSigAlg1, + alg.id, + fakeSigAlg2, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)), + "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)), + "-enable-all-curves", + }, + shouldFail: shouldSignFail, + expectedError: signError, + expectedPeerSignatureAlgorithm: alg.id, + }) + + testCases = append(testCases, testCase{ + name: "ServerAuth-Verify" + suffix, + config: Config{ + MaxVersion: ver.version, + Certificates: []Certificate{getRunnerCertificate(alg.cert)}, + CipherSuites: signingCiphers, + SignSignatureAlgorithms: []signatureAlgorithm{ + alg.id, + }, + Bugs: ProtocolBugs{ + SkipECDSACurveCheck: shouldVerifyFail, + IgnoreSignatureVersionChecks: shouldVerifyFail, + // Some signature algorithms may not be advertised. + IgnorePeerSignatureAlgorithmPreferences: shouldVerifyFail, + }, + }, + flags: []string{ + "-expect-peer-signature-algorithm", strconv.Itoa(int(alg.id)), + "-enable-all-curves", + }, + // Resume the session to assert the peer signature + // algorithm is reported on both handshakes. + resumeSession: !shouldVerifyFail, + shouldFail: shouldVerifyFail, + expectedError: verifyError, + }) + + if !shouldVerifyFail { + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ClientAuth-InvalidSignature" + suffix, + config: Config{ + MaxVersion: ver.version, + Certificates: []Certificate{getRunnerCertificate(alg.cert)}, + SignSignatureAlgorithms: []signatureAlgorithm{ + alg.id, + }, + Bugs: ProtocolBugs{ + InvalidSignature: true, + }, + }, + flags: []string{ + "-require-any-client-certificate", + "-enable-all-curves", + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + }) + + testCases = append(testCases, testCase{ + name: "ServerAuth-InvalidSignature" + suffix, + config: Config{ + MaxVersion: ver.version, + Certificates: []Certificate{getRunnerCertificate(alg.cert)}, + CipherSuites: signingCiphers, + SignSignatureAlgorithms: []signatureAlgorithm{ + alg.id, + }, + Bugs: ProtocolBugs{ + InvalidSignature: true, + }, + }, + flags: []string{"-enable-all-curves"}, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + }) + } + + if ver.version >= VersionTLS12 && !shouldSignFail { + testCases = append(testCases, testCase{ + name: "ClientAuth-Sign-Negotiate" + suffix, + config: Config{ + MaxVersion: ver.version, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: allAlgorithms, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)), + "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)), + "-enable-all-curves", + "-signing-prefs", strconv.Itoa(int(alg.id)), + }, + expectedPeerSignatureAlgorithm: alg.id, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerAuth-Sign-Negotiate" + suffix, + config: Config{ + MaxVersion: ver.version, + CipherSuites: signingCiphers, + VerifySignatureAlgorithms: allAlgorithms, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)), + "-key-file", path.Join(*resourceDir, getShimKey(alg.cert)), + "-enable-all-curves", + "-signing-prefs", strconv.Itoa(int(alg.id)), + }, + expectedPeerSignatureAlgorithm: alg.id, + }) + } + } + } + + // Test that algorithm selection takes the key type into account. + testCases = append(testCases, testCase{ + name: "ClientAuth-SignatureType", + config: Config{ + ClientAuth: RequireAnyClientCert, + MaxVersion: VersionTLS12, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP521AndSHA512, + signatureRSAPKCS1WithSHA384, + signatureECDSAWithSHA1, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA384, + }) + + testCases = append(testCases, testCase{ + name: "ClientAuth-SignatureType-TLS13", + config: Config{ + ClientAuth: RequireAnyClientCert, + MaxVersion: VersionTLS13, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP521AndSHA512, + signatureRSAPKCS1WithSHA384, + signatureRSAPSSWithSHA384, + signatureECDSAWithSHA1, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + expectedPeerSignatureAlgorithm: signatureRSAPSSWithSHA384, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerAuth-SignatureType", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP521AndSHA512, + signatureRSAPKCS1WithSHA384, + signatureECDSAWithSHA1, + }, + }, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA384, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerAuth-SignatureType-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP521AndSHA512, + signatureRSAPKCS1WithSHA384, + signatureRSAPSSWithSHA384, + signatureECDSAWithSHA1, + }, + }, + expectedPeerSignatureAlgorithm: signatureRSAPSSWithSHA384, + }) + + // Test that signature verification takes the key type into account. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Verify-ClientAuth-SignatureType", + config: Config{ + MaxVersion: VersionTLS12, + Certificates: []Certificate{rsaCertificate}, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA256, + }, + Bugs: ProtocolBugs{ + SendSignatureAlgorithm: signatureECDSAWithP256AndSHA256, + }, + }, + flags: []string{ + "-require-any-client-certificate", + }, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Verify-ClientAuth-SignatureType-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{rsaCertificate}, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPSSWithSHA256, + }, + Bugs: ProtocolBugs{ + SendSignatureAlgorithm: signatureECDSAWithP256AndSHA256, + }, + }, + flags: []string{ + "-require-any-client-certificate", + }, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + + testCases = append(testCases, testCase{ + name: "Verify-ServerAuth-SignatureType", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA256, + }, + Bugs: ProtocolBugs{ + SendSignatureAlgorithm: signatureECDSAWithP256AndSHA256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + + testCases = append(testCases, testCase{ + name: "Verify-ServerAuth-SignatureType-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPSSWithSHA256, + }, + Bugs: ProtocolBugs{ + SendSignatureAlgorithm: signatureECDSAWithP256AndSHA256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + + // Test that, if the list is missing, the peer falls back to SHA-1 in + // TLS 1.2, but not TLS 1.3. + testCases = append(testCases, testCase{ + name: "ClientAuth-SHA1-Fallback-RSA", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, + }, + Bugs: ProtocolBugs{ + NoSignatureAlgorithms: true, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerAuth-SHA1-Fallback-RSA", + config: Config{ + MaxVersion: VersionTLS12, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, + }, + Bugs: ProtocolBugs{ + NoSignatureAlgorithms: true, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + }) + + testCases = append(testCases, testCase{ + name: "ClientAuth-SHA1-Fallback-ECDSA", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithSHA1, + }, + Bugs: ProtocolBugs{ + NoSignatureAlgorithms: true, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile), + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerAuth-SHA1-Fallback-ECDSA", + config: Config{ + MaxVersion: VersionTLS12, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithSHA1, + }, + Bugs: ProtocolBugs{ + NoSignatureAlgorithms: true, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile), + }, + }) + + testCases = append(testCases, testCase{ + name: "ClientAuth-NoFallback-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, + }, + Bugs: ProtocolBugs{ + NoSignatureAlgorithms: true, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + shouldFail: true, + // An empty CertificateRequest signature algorithm list is a + // syntax error in TLS 1.3. + expectedError: ":DECODE_ERROR:", + expectedLocalError: "remote error: error decoding message", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerAuth-NoFallback-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, + }, + Bugs: ProtocolBugs{ + NoSignatureAlgorithms: true, + }, + }, + shouldFail: true, + expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", + }) + + // Test that hash preferences are enforced. BoringSSL does not implement + // MD5 signatures. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ClientAuth-Enforced", + config: Config{ + MaxVersion: VersionTLS12, + Certificates: []Certificate{rsaCertificate}, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithMD5, + }, + Bugs: ProtocolBugs{ + IgnorePeerSignatureAlgorithmPreferences: true, + }, + }, + flags: []string{"-require-any-client-certificate"}, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + + testCases = append(testCases, testCase{ + name: "ServerAuth-Enforced", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithMD5, + }, + Bugs: ProtocolBugs{ + IgnorePeerSignatureAlgorithmPreferences: true, + }, + }, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ClientAuth-Enforced-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{rsaCertificate}, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithMD5, + }, + Bugs: ProtocolBugs{ + IgnorePeerSignatureAlgorithmPreferences: true, + IgnoreSignatureVersionChecks: true, + }, + }, + flags: []string{"-require-any-client-certificate"}, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + + testCases = append(testCases, testCase{ + name: "ServerAuth-Enforced-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithMD5, + }, + Bugs: ProtocolBugs{ + IgnorePeerSignatureAlgorithmPreferences: true, + IgnoreSignatureVersionChecks: true, + }, + }, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + + // Test that the agreed upon digest respects the client preferences and + // the server digests. + testCases = append(testCases, testCase{ + name: "NoCommonAlgorithms-Digests", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA512, + signatureRSAPKCS1WithSHA1, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-digest-prefs", "SHA256", + }, + shouldFail: true, + expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", + }) + testCases = append(testCases, testCase{ + name: "NoCommonAlgorithms", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA512, + signatureRSAPKCS1WithSHA1, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)), + }, + shouldFail: true, + expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", + }) + testCases = append(testCases, testCase{ + name: "NoCommonAlgorithms-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPSSWithSHA512, + signatureRSAPSSWithSHA384, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-signing-prefs", strconv.Itoa(int(signatureRSAPSSWithSHA256)), + }, + shouldFail: true, + expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", + }) + testCases = append(testCases, testCase{ + name: "Agree-Digest-SHA256", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, + signatureRSAPKCS1WithSHA256, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-digest-prefs", "SHA256,SHA1", + }, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256, + }) + testCases = append(testCases, testCase{ + name: "Agree-Digest-SHA1", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA1, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-digest-prefs", "SHA512,SHA256,SHA1", + }, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA1, + }) + testCases = append(testCases, testCase{ + name: "Agree-Digest-Default", + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA256, + signatureECDSAWithP256AndSHA256, + signatureRSAPKCS1WithSHA1, + signatureECDSAWithSHA1, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256, + }) + + // Test that the signing preference list may include extra algorithms + // without negotiation problems. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "FilterExtraAlgorithms", + config: Config{ + MaxVersion: VersionTLS12, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPKCS1WithSHA256, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + "-signing-prefs", strconv.Itoa(int(fakeSigAlg1)), + "-signing-prefs", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)), + "-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)), + "-signing-prefs", strconv.Itoa(int(fakeSigAlg2)), + }, + expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256, + }) + + // In TLS 1.2 and below, ECDSA uses the curve list rather than the + // signature algorithms. + testCases = append(testCases, testCase{ + name: "CheckLeafCurve", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{ecdsaP256Certificate}, + }, + flags: []string{"-p384-only"}, + shouldFail: true, + expectedError: ":BAD_ECC_CERT:", + }) + + // In TLS 1.3, ECDSA does not use the ECDHE curve list. + testCases = append(testCases, testCase{ + name: "CheckLeafCurve-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{ecdsaP256Certificate}, + }, + flags: []string{"-p384-only"}, + }) + + // In TLS 1.2, the ECDSA curve is not in the signature algorithm. + testCases = append(testCases, testCase{ + name: "ECDSACurveMismatch-Verify-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{ecdsaP256Certificate}, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP384AndSHA384, + }, + }, + }) + + // In TLS 1.3, the ECDSA curve comes from the signature algorithm. + testCases = append(testCases, testCase{ + name: "ECDSACurveMismatch-Verify-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Certificates: []Certificate{ecdsaP256Certificate}, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP384AndSHA384, + }, + Bugs: ProtocolBugs{ + SkipECDSACurveCheck: true, + }, + }, + shouldFail: true, + expectedError: ":WRONG_SIGNATURE_TYPE:", + }) + + // Signature algorithm selection in TLS 1.3 should take the curve into + // account. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ECDSACurveMismatch-Sign-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureECDSAWithP384AndSHA384, + signatureECDSAWithP256AndSHA256, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, ecdsaP256CertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaP256KeyFile), + }, + expectedPeerSignatureAlgorithm: signatureECDSAWithP256AndSHA256, + }) + + // RSASSA-PSS with SHA-512 is too large for 1024-bit RSA. Test that the + // server does not attempt to sign in that case. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "RSA-PSS-Large", + config: Config{ + MaxVersion: VersionTLS13, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPSSWithSHA512, + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsa1024CertificateFile), + "-key-file", path.Join(*resourceDir, rsa1024KeyFile), + }, + shouldFail: true, + expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:", + }) + + // Test that RSA-PSS is enabled by default for TLS 1.2. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "RSA-PSS-Default-Verify", + config: Config{ + MaxVersion: VersionTLS12, + SignSignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPSSWithSHA256, + }, + }, + flags: []string{"-max-version", strconv.Itoa(VersionTLS12)}, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "RSA-PSS-Default-Sign", + config: Config{ + MaxVersion: VersionTLS12, + VerifySignatureAlgorithms: []signatureAlgorithm{ + signatureRSAPSSWithSHA256, + }, + }, + flags: []string{"-max-version", strconv.Itoa(VersionTLS12)}, + }) + + // A server certificate with a P-224 key will only work up to TLS 1.2 + // and we only test it with BoringSSL acting as a server because that's + // all Alphabet requires with it. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "P224-Server", + config: Config{ + VerifySignatureAlgorithms: []signatureAlgorithm{ + // TLS 1.2 does not require that the curve + // match the hash, thus P-256 with SHA-256 is + // the same signature algorithm value as P-224 + // with SHA-256. + signatureECDSAWithP256AndSHA256, + }, + // P-256 must be offered as well because ECDHE requires + // it. + CurvePreferences: []CurveID{CurveP224, CurveP256}, + }, + flags: []string{ + "-max-version", strconv.Itoa(VersionTLS12), + "-cert-file", path.Join(*resourceDir, ecdsaP224CertificateFile), + "-key-file", path.Join(*resourceDir, ecdsaP224KeyFile), + }, + }) +} + +// timeouts is the retransmit schedule for BoringSSL. It doubles and +// caps at 60 seconds. On the 13th timeout, it gives up. +var timeouts = []time.Duration{ + 1 * time.Second, + 2 * time.Second, + 4 * time.Second, + 8 * time.Second, + 16 * time.Second, + 32 * time.Second, + 60 * time.Second, + 60 * time.Second, + 60 * time.Second, + 60 * time.Second, + 60 * time.Second, + 60 * time.Second, + 60 * time.Second, +} + +// shortTimeouts is an alternate set of timeouts which would occur if the +// initial timeout duration was set to 250ms. +var shortTimeouts = []time.Duration{ + 250 * time.Millisecond, + 500 * time.Millisecond, + 1 * time.Second, + 2 * time.Second, + 4 * time.Second, + 8 * time.Second, + 16 * time.Second, + 32 * time.Second, + 60 * time.Second, + 60 * time.Second, + 60 * time.Second, + 60 * time.Second, + 60 * time.Second, +} + +func addDTLSRetransmitTests() { + // These tests work by coordinating some behavior on both the shim and + // the runner. + // + // TimeoutSchedule configures the runner to send a series of timeout + // opcodes to the shim (see packetAdaptor) immediately before reading + // each peer handshake flight N. The timeout opcode both simulates a + // timeout in the shim and acts as a synchronization point to help the + // runner bracket each handshake flight. + // + // We assume the shim does not read from the channel eagerly. It must + // first wait until it has sent flight N and is ready to receive + // handshake flight N+1. At this point, it will process the timeout + // opcode. It must then immediately respond with a timeout ACK and act + // as if the shim was idle for the specified amount of time. + // + // The runner then drops all packets received before the ACK and + // continues waiting for flight N. This ordering results in one attempt + // at sending flight N to be dropped. For the test to complete, the + // shim must send flight N again, testing that the shim implements DTLS + // retransmit on a timeout. + + // TODO(davidben): Add DTLS 1.3 versions of these tests. There will + // likely be more epochs to cross and the final message's retransmit may + // be more complex. + + // Test that this is indeed the timeout schedule. Stress all + // four patterns of handshake. + for i := 1; i < len(timeouts); i++ { + number := strconv.Itoa(i) + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Retransmit-Client-" + number, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + TimeoutSchedule: timeouts[:i], + }, + }, + resumeSession: true, + flags: []string{"-async"}, + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "DTLS-Retransmit-Server-" + number, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + TimeoutSchedule: timeouts[:i], + }, + }, + resumeSession: true, + flags: []string{"-async"}, + }) + } + + // Test that exceeding the timeout schedule hits a read + // timeout. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Retransmit-Timeout", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + TimeoutSchedule: timeouts, + }, + }, + resumeSession: true, + flags: []string{"-async"}, + shouldFail: true, + expectedError: ":READ_TIMEOUT_EXPIRED:", + }) + + // Test that timeout handling has a fudge factor, due to API + // problems. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Retransmit-Fudge", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + TimeoutSchedule: []time.Duration{ + timeouts[0] - 10*time.Millisecond, + }, + }, + }, + resumeSession: true, + flags: []string{"-async"}, + }) + + // Test that the final Finished retransmitting isn't + // duplicated if the peer badly fragments everything. + testCases = append(testCases, testCase{ + testType: serverTest, + protocol: dtls, + name: "DTLS-Retransmit-Fragmented", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + TimeoutSchedule: []time.Duration{timeouts[0]}, + MaxHandshakeRecordLength: 2, + }, + }, + flags: []string{"-async"}, + }) + + // Test the timeout schedule when a shorter initial timeout duration is set. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Retransmit-Short-Client", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1], + }, + }, + resumeSession: true, + flags: []string{ + "-async", + "-initial-timeout-duration-ms", "250", + }, + }) + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "DTLS-Retransmit-Short-Server", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1], + }, + }, + resumeSession: true, + flags: []string{ + "-async", + "-initial-timeout-duration-ms", "250", + }, + }) +} + +func addExportKeyingMaterialTests() { + for _, vers := range tlsVersions { + if vers.version == VersionSSL30 { + continue + } + testCases = append(testCases, testCase{ + name: "ExportKeyingMaterial-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + exportKeyingMaterial: 1024, + exportLabel: "label", + exportContext: "context", + useExportContext: true, + }) + testCases = append(testCases, testCase{ + name: "ExportKeyingMaterial-NoContext-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + exportKeyingMaterial: 1024, + }) + testCases = append(testCases, testCase{ + name: "ExportKeyingMaterial-EmptyContext-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + exportKeyingMaterial: 1024, + useExportContext: true, + }) + testCases = append(testCases, testCase{ + name: "ExportKeyingMaterial-Small-" + vers.name, + config: Config{ + MaxVersion: vers.version, + }, + exportKeyingMaterial: 1, + exportLabel: "label", + exportContext: "context", + useExportContext: true, + }) + } + + testCases = append(testCases, testCase{ + name: "ExportKeyingMaterial-SSL3", + config: Config{ + MaxVersion: VersionSSL30, + }, + exportKeyingMaterial: 1024, + exportLabel: "label", + exportContext: "context", + useExportContext: true, + shouldFail: true, + expectedError: "failed to export keying material", + }) + + // Exporters work during a False Start. + testCases = append(testCases, testCase{ + name: "ExportKeyingMaterial-FalseStart", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + }, + }, + flags: []string{ + "-false-start", + "-advertise-alpn", "\x03foo", + }, + shimWritesFirst: true, + exportKeyingMaterial: 1024, + exportLabel: "label", + exportContext: "context", + useExportContext: true, + }) + + // Exporters do not work in the middle of a renegotiation. Test this by + // triggering the exporter after every SSL_read call and configuring the + // shim to run asynchronously. + testCases = append(testCases, testCase{ + name: "ExportKeyingMaterial-Renegotiate", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 1, + flags: []string{ + "-async", + "-use-exporter-between-reads", + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + shouldFail: true, + expectedError: "failed to export keying material", + }) +} + +func addTLSUniqueTests() { + for _, isClient := range []bool{false, true} { + for _, isResumption := range []bool{false, true} { + for _, hasEMS := range []bool{false, true} { + var suffix string + if isResumption { + suffix = "Resume-" + } else { + suffix = "Full-" + } + + if hasEMS { + suffix += "EMS-" + } else { + suffix += "NoEMS-" + } + + if isClient { + suffix += "Client" + } else { + suffix += "Server" + } + + test := testCase{ + name: "TLSUnique-" + suffix, + testTLSUnique: true, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: !hasEMS, + }, + }, + } + + if isResumption { + test.resumeSession = true + test.resumeConfig = &Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: !hasEMS, + }, + } + } + + if isResumption && !hasEMS { + test.shouldFail = true + test.expectedError = "failed to get tls-unique" + } + + testCases = append(testCases, test) + } + } + } +} + +func addCustomExtensionTests() { + expectedContents := "custom extension" + emptyString := "" + + for _, isClient := range []bool{false, true} { + suffix := "Server" + flag := "-enable-server-custom-extension" + testType := serverTest + if isClient { + suffix = "Client" + flag = "-enable-client-custom-extension" + testType = clientTest + } + + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag}, + }) + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-" + suffix + "-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag}, + }) + + // If the parse callback fails, the handshake should also fail. + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-ParseError-" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents + "foo", + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag}, + shouldFail: true, + expectedError: ":CUSTOM_EXTENSION_ERROR:", + }) + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-ParseError-" + suffix + "-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents + "foo", + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag}, + shouldFail: true, + expectedError: ":CUSTOM_EXTENSION_ERROR:", + }) + + // If the add callback fails, the handshake should also fail. + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-FailAdd-" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag, "-custom-extension-fail-add"}, + shouldFail: true, + expectedError: ":CUSTOM_EXTENSION_ERROR:", + }) + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-FailAdd-" + suffix + "-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + ExpectedCustomExtension: &expectedContents, + }, + }, + flags: []string{flag, "-custom-extension-fail-add"}, + shouldFail: true, + expectedError: ":CUSTOM_EXTENSION_ERROR:", + }) + + // If the add callback returns zero, no extension should be + // added. + skipCustomExtension := expectedContents + if isClient { + // For the case where the client skips sending the + // custom extension, the server must not “echo” it. + skipCustomExtension = "" + } + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-Skip-" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + CustomExtension: skipCustomExtension, + ExpectedCustomExtension: &emptyString, + }, + }, + flags: []string{flag, "-custom-extension-skip"}, + }) + testCases = append(testCases, testCase{ + testType: testType, + name: "CustomExtensions-Skip-" + suffix + "-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + CustomExtension: skipCustomExtension, + ExpectedCustomExtension: &emptyString, + }, + }, + flags: []string{flag, "-custom-extension-skip"}, + }) + } + + // The custom extension add callback should not be called if the client + // doesn't send the extension. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CustomExtensions-NotCalled-Server", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + ExpectedCustomExtension: &emptyString, + }, + }, + flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"}, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CustomExtensions-NotCalled-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExpectedCustomExtension: &emptyString, + }, + }, + flags: []string{"-enable-server-custom-extension", "-custom-extension-fail-add"}, + }) + + // Test an unknown extension from the server. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "UnknownExtension-Client", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + expectedLocalError: "remote error: unsupported extension", + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "UnknownExtension-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + CustomExtension: expectedContents, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + expectedLocalError: "remote error: unsupported extension", + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "UnknownUnencryptedExtension-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + CustomUnencryptedExtension: expectedContents, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + // The shim must send an alert, but alerts at this point do not + // get successfully decrypted by the runner. + expectedLocalError: "local error: bad record MAC", + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "UnexpectedUnencryptedExtension-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendUnencryptedALPN: "foo", + }, + }, + flags: []string{ + "-advertise-alpn", "\x03foo\x03bar", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + // The shim must send an alert, but alerts at this point do not + // get successfully decrypted by the runner. + expectedLocalError: "local error: bad record MAC", + }) + + // Test a known but unoffered extension from the server. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "UnofferedExtension-Client", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendALPN: "alpn", + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + expectedLocalError: "remote error: unsupported extension", + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "UnofferedExtension-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendALPN: "alpn", + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + expectedLocalError: "remote error: unsupported extension", + }) +} + +func addRSAClientKeyExchangeTests() { + for bad := RSABadValue(1); bad < NumRSABadValues; bad++ { + testCases = append(testCases, testCase{ + testType: serverTest, + name: fmt.Sprintf("BadRSAClientKeyExchange-%d", bad), + config: Config{ + // Ensure the ClientHello version and final + // version are different, to detect if the + // server uses the wrong one. + MaxVersion: VersionTLS11, + CipherSuites: []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA}, + Bugs: ProtocolBugs{ + BadRSAClientKeyExchange: bad, + }, + }, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }) + } + + // The server must compare whatever was in ClientHello.version for the + // RSA premaster. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SendClientVersion-RSA", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SendClientVersion: 0x1234, + }, + }, + flags: []string{"-max-version", strconv.Itoa(VersionTLS12)}, + }) +} + +var testCurves = []struct { + name string + id CurveID +}{ + {"P-256", CurveP256}, + {"P-384", CurveP384}, + {"P-521", CurveP521}, + {"X25519", CurveX25519}, +} + +const bogusCurve = 0x1234 + +func addCurveTests() { + for _, curve := range testCurves { + testCases = append(testCases, testCase{ + name: "CurveTest-Client-" + curve.name, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{curve.id}, + }, + flags: []string{ + "-enable-all-curves", + "-expect-curve-id", strconv.Itoa(int(curve.id)), + }, + expectedCurveID: curve.id, + }) + testCases = append(testCases, testCase{ + name: "CurveTest-Client-" + curve.name + "-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{curve.id}, + }, + flags: []string{ + "-enable-all-curves", + "-expect-curve-id", strconv.Itoa(int(curve.id)), + }, + expectedCurveID: curve.id, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CurveTest-Server-" + curve.name, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{curve.id}, + }, + flags: []string{ + "-enable-all-curves", + "-expect-curve-id", strconv.Itoa(int(curve.id)), + }, + expectedCurveID: curve.id, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CurveTest-Server-" + curve.name + "-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{curve.id}, + }, + flags: []string{ + "-enable-all-curves", + "-expect-curve-id", strconv.Itoa(int(curve.id)), + }, + expectedCurveID: curve.id, + }) + } + + // The server must be tolerant to bogus curves. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "UnknownCurve", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{bogusCurve, CurveP256}, + }, + }) + + // The server must be tolerant to bogus curves. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "UnknownCurve-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{bogusCurve, CurveP256}, + }, + }) + + // The server must not consider ECDHE ciphers when there are no + // supported curves. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "NoSupportedCurves", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + NoSupportedCurves: true, + }, + }, + shouldFail: true, + expectedError: ":NO_SHARED_CIPHER:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "NoSupportedCurves-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + NoSupportedCurves: true, + }, + }, + shouldFail: true, + expectedError: ":NO_SHARED_GROUP:", + }) + + // The server must fall back to another cipher when there are no + // supported curves. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "NoCommonCurves", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + }, + CurvePreferences: []CurveID{CurveP224}, + }, + expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + }) + + // The client must reject bogus curves and disabled curves. + testCases = append(testCases, testCase{ + name: "BadECDHECurve", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SendCurve: bogusCurve, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + testCases = append(testCases, testCase{ + name: "BadECDHECurve-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendCurve: bogusCurve, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + testCases = append(testCases, testCase{ + name: "UnsupportedCurve", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + IgnorePeerCurvePreferences: true, + }, + }, + flags: []string{"-p384-only"}, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + testCases = append(testCases, testCase{ + // TODO(davidben): Add a TLS 1.3 version where + // HelloRetryRequest requests an unsupported curve. + name: "UnsupportedCurve-ServerHello-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + SendCurve: CurveP256, + }, + }, + flags: []string{"-p384-only"}, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + // Test invalid curve points. + testCases = append(testCases, testCase{ + name: "InvalidECDHPoint-Client", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + InvalidECDHPoint: true, + }, + }, + shouldFail: true, + expectedError: ":INVALID_ENCODING:", + }) + testCases = append(testCases, testCase{ + name: "InvalidECDHPoint-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + InvalidECDHPoint: true, + }, + }, + shouldFail: true, + expectedError: ":INVALID_ENCODING:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "InvalidECDHPoint-Server", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + InvalidECDHPoint: true, + }, + }, + shouldFail: true, + expectedError: ":INVALID_ENCODING:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "InvalidECDHPoint-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + InvalidECDHPoint: true, + }, + }, + shouldFail: true, + expectedError: ":INVALID_ENCODING:", + }) + + // The previous curve ID should be reported on TLS 1.2 resumption. + testCases = append(testCases, testCase{ + name: "CurveID-Resume-Client", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{CurveX25519}, + }, + flags: []string{"-expect-curve-id", strconv.Itoa(int(CurveX25519))}, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CurveID-Resume-Server", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + CurvePreferences: []CurveID{CurveX25519}, + }, + flags: []string{"-expect-curve-id", strconv.Itoa(int(CurveX25519))}, + resumeSession: true, + }) + + // TLS 1.3 allows resuming at a differet curve. If this happens, the new + // one should be reported. + testCases = append(testCases, testCase{ + name: "CurveID-Resume-Client-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveX25519}, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveP256}, + }, + flags: []string{ + "-expect-curve-id", strconv.Itoa(int(CurveX25519)), + "-expect-resume-curve-id", strconv.Itoa(int(CurveP256)), + }, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CurveID-Resume-Server-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveX25519}, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveP256}, + }, + flags: []string{ + "-expect-curve-id", strconv.Itoa(int(CurveX25519)), + "-expect-resume-curve-id", strconv.Itoa(int(CurveP256)), + }, + resumeSession: true, + }) + + // Server-sent point formats are legal in TLS 1.2, but not in TLS 1.3. + testCases = append(testCases, testCase{ + name: "PointFormat-ServerHello-TLS12", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendSupportedPointFormats: []byte{pointFormatUncompressed}, + }, + }, + }) + testCases = append(testCases, testCase{ + name: "PointFormat-EncryptedExtensions-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendSupportedPointFormats: []byte{pointFormatUncompressed}, + }, + }, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) + + // Test that we tolerate unknown point formats, as long as + // pointFormatUncompressed is present. Limit ciphers to ECDHE ciphers to + // check they are still functional. + testCases = append(testCases, testCase{ + name: "PointFormat-Client-Tolerance", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendSupportedPointFormats: []byte{42, pointFormatUncompressed, 99, pointFormatCompressedPrime}, + }, + }, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "PointFormat-Server-Tolerance", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, + Bugs: ProtocolBugs{ + SendSupportedPointFormats: []byte{42, pointFormatUncompressed, 99, pointFormatCompressedPrime}, + }, + }, + }) + + // Test TLS 1.2 does not require the point format extension to be + // present. + testCases = append(testCases, testCase{ + name: "PointFormat-Client-Missing", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, + Bugs: ProtocolBugs{ + SendSupportedPointFormats: []byte{}, + }, + }, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "PointFormat-Server-Missing", + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, + Bugs: ProtocolBugs{ + SendSupportedPointFormats: []byte{}, + }, + }, + }) + + // If the point format extension is present, uncompressed points must be + // offered. BoringSSL requires this whether or not ECDHE is used. + testCases = append(testCases, testCase{ + name: "PointFormat-Client-MissingUncompressed", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendSupportedPointFormats: []byte{pointFormatCompressedPrime}, + }, + }, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "PointFormat-Server-MissingUncompressed", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendSupportedPointFormats: []byte{pointFormatCompressedPrime}, + }, + }, + shouldFail: true, + expectedError: ":ERROR_PARSING_EXTENSION:", + }) +} + +func addTLS13RecordTests() { + testCases = append(testCases, testCase{ + name: "TLS13-RecordPadding", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + Bugs: ProtocolBugs{ + RecordPadding: 10, + }, + }, + }) + + testCases = append(testCases, testCase{ + name: "TLS13-EmptyRecords", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + Bugs: ProtocolBugs{ + OmitRecordContents: true, + }, + }, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }) + + testCases = append(testCases, testCase{ + name: "TLS13-OnlyPadding", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + Bugs: ProtocolBugs{ + OmitRecordContents: true, + RecordPadding: 10, + }, + }, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }) + + testCases = append(testCases, testCase{ + name: "TLS13-WrongOuterRecord", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + Bugs: ProtocolBugs{ + OuterRecordType: recordTypeHandshake, + }, + }, + shouldFail: true, + expectedError: ":INVALID_OUTER_RECORD_TYPE:", + }) +} + +func addSessionTicketTests() { + testCases = append(testCases, testCase{ + // In TLS 1.2 and below, empty NewSessionTicket messages + // mean the server changed its mind on sending a ticket. + name: "SendEmptySessionTicket", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendEmptySessionTicket: true, + }, + }, + flags: []string{"-expect-no-session"}, + }) + + // Test that the server ignores unknown PSK modes. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-SendUnknownModeSessionTicket-Server", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendPSKKeyExchangeModes: []byte{0x1a, pskDHEKEMode, 0x2a}, + }, + }, + resumeSession: true, + expectedResumeVersion: VersionTLS13, + }) + + // Test that the server does not send session tickets with no matching key exchange mode. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-ExpectNoSessionTicketOnBadKEMode-Server", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendPSKKeyExchangeModes: []byte{0x1a}, + ExpectNoNewSessionTicket: true, + }, + }, + }) + + // Test that the server does not accept a session with no matching key exchange mode. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-SendBadKEModeSessionTicket-Server", + config: Config{ + MaxVersion: VersionTLS13, + }, + resumeConfig: &Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendPSKKeyExchangeModes: []byte{0x1a}, + }, + }, + resumeSession: true, + expectResumeRejected: true, + }) + + // Test that the client ticket age is sent correctly. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13-TestValidTicketAge-Client", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExpectTicketAge: 10 * time.Second, + }, + }, + resumeSession: true, + flags: []string{ + "-resumption-delay", "10", + }, + }) + + // Test that the client ticket age is enforced. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13-TestBadTicketAge-Client", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExpectTicketAge: 1000 * time.Second, + }, + }, + resumeSession: true, + shouldFail: true, + expectedLocalError: "tls: invalid ticket age", + }) + + // Test that the server's ticket age skew reporting works. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-TicketAgeSkew-Forward", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendTicketAge: 15 * time.Second, + }, + }, + resumeSession: true, + flags: []string{ + "-resumption-delay", "10", + "-expect-ticket-age-skew", "5", + }, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-TicketAgeSkew-Backward", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendTicketAge: 5 * time.Second, + }, + }, + resumeSession: true, + flags: []string{ + "-resumption-delay", "10", + "-expect-ticket-age-skew", "-5", + }, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13-SendTicketEarlyDataInfo", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + }, + flags: []string{ + "-enable-early-data", + "-expect-early-data-info", + }, + }) + + // Test that 0-RTT tickets are ignored in clients unless opted in. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13-SendTicketEarlyDataInfo-Disabled", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + }, + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13-DuplicateTicketEarlyDataInfo", + config: Config{ + MaxVersion: VersionTLS13, + MaxEarlyDataSize: 16384, + Bugs: ProtocolBugs{ + DuplicateTicketEarlyDataInfo: true, + }, + }, + shouldFail: true, + expectedError: ":DUPLICATE_EXTENSION:", + expectedLocalError: "remote error: illegal parameter", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-ExpectTicketEarlyDataInfo", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExpectTicketEarlyDataInfo: true, + }, + }, + flags: []string{ + "-enable-early-data", + }, + }) + + // Test that, in TLS 1.3, the server-offered NewSessionTicket lifetime + // is honored. + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13-HonorServerSessionTicketLifetime", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendTicketLifetime: 20 * time.Second, + }, + }, + flags: []string{ + "-resumption-delay", "19", + }, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "TLS13-HonorServerSessionTicketLifetime-2", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendTicketLifetime: 20 * time.Second, + // The client should not offer the expired session. + ExpectNoTLS13PSK: true, + }, + }, + flags: []string{ + "-resumption-delay", "21", + }, + resumeSession: true, + expectResumeRejected: true, + }) +} + +func addChangeCipherSpecTests() { + // Test missing ChangeCipherSpecs. + testCases = append(testCases, testCase{ + name: "SkipChangeCipherSpec-Client", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipChangeCipherSpec-Server", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipChangeCipherSpec-Server-NPN", + config: Config{ + MaxVersion: VersionTLS12, + NextProtos: []string{"bar"}, + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + flags: []string{ + "-advertise-npn", "\x03foo\x03bar\x03baz", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + + // Test synchronization between the handshake and ChangeCipherSpec. + // Partial post-CCS handshake messages before ChangeCipherSpec should be + // rejected. Test both with and without handshake packing to handle both + // when the partial post-CCS message is in its own record and when it is + // attached to the pre-CCS message. + for _, packed := range []bool{false, true} { + var suffix string + if packed { + suffix = "-Packed" + } + + testCases = append(testCases, testCase{ + name: "FragmentAcrossChangeCipherSpec-Client" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + PackHandshakeFlight: packed, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + testCases = append(testCases, testCase{ + name: "FragmentAcrossChangeCipherSpec-Client-Resume" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + resumeSession: true, + resumeConfig: &Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + PackHandshakeFlight: packed, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "FragmentAcrossChangeCipherSpec-Server" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + PackHandshakeFlight: packed, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "FragmentAcrossChangeCipherSpec-Server-Resume" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + resumeSession: true, + resumeConfig: &Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + PackHandshakeFlight: packed, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "FragmentAcrossChangeCipherSpec-Server-NPN" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + NextProtos: []string{"bar"}, + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + PackHandshakeFlight: packed, + }, + }, + flags: []string{ + "-advertise-npn", "\x03foo\x03bar\x03baz", + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + } + + // Test that, in DTLS, ChangeCipherSpec is not allowed when there are + // messages in the handshake queue. Do this by testing the server + // reading the client Finished, reversing the flight so Finished comes + // first. + testCases = append(testCases, testCase{ + protocol: dtls, + testType: serverTest, + name: "SendUnencryptedFinished-DTLS", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + SendUnencryptedFinished: true, + ReverseHandshakeFragments: true, + }, + }, + shouldFail: true, + expectedError: ":BUFFERED_MESSAGES_ON_CIPHER_CHANGE:", + }) + + // Test synchronization between encryption changes and the handshake in + // TLS 1.3, where ChangeCipherSpec is implicit. + testCases = append(testCases, testCase{ + name: "PartialEncryptedExtensionsWithServerHello", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + PartialEncryptedExtensionsWithServerHello: true, + }, + }, + shouldFail: true, + expectedError: ":BUFFERED_MESSAGES_ON_CIPHER_CHANGE:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "PartialClientFinishedWithClientHello", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + PartialClientFinishedWithClientHello: true, + }, + }, + shouldFail: true, + expectedError: ":BUFFERED_MESSAGES_ON_CIPHER_CHANGE:", + }) + + // Test that early ChangeCipherSpecs are handled correctly. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "EarlyChangeCipherSpec-server-1", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + EarlyChangeCipherSpec: 1, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "EarlyChangeCipherSpec-server-2", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + EarlyChangeCipherSpec: 2, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + testCases = append(testCases, testCase{ + protocol: dtls, + name: "StrayChangeCipherSpec", + config: Config{ + // TODO(davidben): Once DTLS 1.3 exists, test + // that stray ChangeCipherSpec messages are + // rejected. + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + StrayChangeCipherSpec: true, + }, + }, + }) + + // Test that the contents of ChangeCipherSpec are checked. + testCases = append(testCases, testCase{ + name: "BadChangeCipherSpec-1", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadChangeCipherSpec: []byte{2}, + }, + }, + shouldFail: true, + expectedError: ":BAD_CHANGE_CIPHER_SPEC:", + }) + testCases = append(testCases, testCase{ + name: "BadChangeCipherSpec-2", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadChangeCipherSpec: []byte{1, 1}, + }, + }, + shouldFail: true, + expectedError: ":BAD_CHANGE_CIPHER_SPEC:", + }) + testCases = append(testCases, testCase{ + protocol: dtls, + name: "BadChangeCipherSpec-DTLS-1", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadChangeCipherSpec: []byte{2}, + }, + }, + shouldFail: true, + expectedError: ":BAD_CHANGE_CIPHER_SPEC:", + }) + testCases = append(testCases, testCase{ + protocol: dtls, + name: "BadChangeCipherSpec-DTLS-2", + config: Config{ + MaxVersion: VersionTLS12, + Bugs: ProtocolBugs{ + BadChangeCipherSpec: []byte{1, 1}, + }, + }, + shouldFail: true, + expectedError: ":BAD_CHANGE_CIPHER_SPEC:", + }) +} + +type perMessageTest struct { + messageType uint8 + test testCase +} + +// makePerMessageTests returns a series of test templates which cover each +// message in the TLS handshake. These may be used with bugs like +// WrongMessageType to fully test a per-message bug. +func makePerMessageTests() []perMessageTest { + var ret []perMessageTest + for _, protocol := range []protocol{tls, dtls} { + var suffix string + if protocol == dtls { + suffix = "-DTLS" + } + + ret = append(ret, perMessageTest{ + messageType: typeClientHello, + test: testCase{ + protocol: protocol, + testType: serverTest, + name: "ClientHello" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + + if protocol == dtls { + ret = append(ret, perMessageTest{ + messageType: typeHelloVerifyRequest, + test: testCase{ + protocol: protocol, + name: "HelloVerifyRequest" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + } + + ret = append(ret, perMessageTest{ + messageType: typeServerHello, + test: testCase{ + protocol: protocol, + name: "ServerHello" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificate, + test: testCase{ + protocol: protocol, + name: "ServerCertificate" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificateStatus, + test: testCase{ + protocol: protocol, + name: "CertificateStatus" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + flags: []string{"-enable-ocsp-stapling"}, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeServerKeyExchange, + test: testCase{ + protocol: protocol, + name: "ServerKeyExchange" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificateRequest, + test: testCase{ + protocol: protocol, + name: "CertificateRequest" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + ClientAuth: RequireAnyClientCert, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeServerHelloDone, + test: testCase{ + protocol: protocol, + name: "ServerHelloDone" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificate, + test: testCase{ + testType: serverTest, + protocol: protocol, + name: "ClientCertificate" + suffix, + config: Config{ + Certificates: []Certificate{rsaCertificate}, + MaxVersion: VersionTLS12, + }, + flags: []string{"-require-any-client-certificate"}, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificateVerify, + test: testCase{ + testType: serverTest, + protocol: protocol, + name: "CertificateVerify" + suffix, + config: Config{ + Certificates: []Certificate{rsaCertificate}, + MaxVersion: VersionTLS12, + }, + flags: []string{"-require-any-client-certificate"}, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeClientKeyExchange, + test: testCase{ + testType: serverTest, + protocol: protocol, + name: "ClientKeyExchange" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + + if protocol != dtls { + ret = append(ret, perMessageTest{ + messageType: typeNextProtocol, + test: testCase{ + testType: serverTest, + protocol: protocol, + name: "NextProtocol" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + NextProtos: []string{"bar"}, + }, + flags: []string{"-advertise-npn", "\x03foo\x03bar\x03baz"}, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeChannelID, + test: testCase{ + testType: serverTest, + protocol: protocol, + name: "ChannelID" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + ChannelID: channelIDKey, + }, + flags: []string{ + "-expect-channel-id", + base64.StdEncoding.EncodeToString(channelIDBytes), + }, + }, + }) + } + + ret = append(ret, perMessageTest{ + messageType: typeFinished, + test: testCase{ + testType: serverTest, + protocol: protocol, + name: "ClientFinished" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeNewSessionTicket, + test: testCase{ + protocol: protocol, + name: "NewSessionTicket" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeFinished, + test: testCase{ + protocol: protocol, + name: "ServerFinished" + suffix, + config: Config{ + MaxVersion: VersionTLS12, + }, + }, + }) + + } + + ret = append(ret, perMessageTest{ + messageType: typeClientHello, + test: testCase{ + testType: serverTest, + name: "TLS13-ClientHello", + config: Config{ + MaxVersion: VersionTLS13, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeServerHello, + test: testCase{ + name: "TLS13-ServerHello", + config: Config{ + MaxVersion: VersionTLS13, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeEncryptedExtensions, + test: testCase{ + name: "TLS13-EncryptedExtensions", + config: Config{ + MaxVersion: VersionTLS13, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificateRequest, + test: testCase{ + name: "TLS13-CertificateRequest", + config: Config{ + MaxVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificate, + test: testCase{ + name: "TLS13-ServerCertificate", + config: Config{ + MaxVersion: VersionTLS13, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificateVerify, + test: testCase{ + name: "TLS13-ServerCertificateVerify", + config: Config{ + MaxVersion: VersionTLS13, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeFinished, + test: testCase{ + name: "TLS13-ServerFinished", + config: Config{ + MaxVersion: VersionTLS13, + }, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificate, + test: testCase{ + testType: serverTest, + name: "TLS13-ClientCertificate", + config: Config{ + Certificates: []Certificate{rsaCertificate}, + MaxVersion: VersionTLS13, + }, + flags: []string{"-require-any-client-certificate"}, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeCertificateVerify, + test: testCase{ + testType: serverTest, + name: "TLS13-ClientCertificateVerify", + config: Config{ + Certificates: []Certificate{rsaCertificate}, + MaxVersion: VersionTLS13, + }, + flags: []string{"-require-any-client-certificate"}, + }, + }) + + ret = append(ret, perMessageTest{ + messageType: typeFinished, + test: testCase{ + testType: serverTest, + name: "TLS13-ClientFinished", + config: Config{ + MaxVersion: VersionTLS13, + }, + }, + }) + + return ret +} + +func addWrongMessageTypeTests() { + for _, t := range makePerMessageTests() { + t.test.name = "WrongMessageType-" + t.test.name + t.test.config.Bugs.SendWrongMessageType = t.messageType + t.test.shouldFail = true + t.test.expectedError = ":UNEXPECTED_MESSAGE:" + t.test.expectedLocalError = "remote error: unexpected message" + + if t.test.config.MaxVersion >= VersionTLS13 && t.messageType == typeServerHello { + // In TLS 1.3, a bad ServerHello means the client sends + // an unencrypted alert while the server expects + // encryption, so the alert is not readable by runner. + t.test.expectedLocalError = "local error: bad record MAC" + } + + testCases = append(testCases, t.test) + } +} + +func addTrailingMessageDataTests() { + for _, t := range makePerMessageTests() { + t.test.name = "TrailingMessageData-" + t.test.name + t.test.config.Bugs.SendTrailingMessageData = t.messageType + t.test.shouldFail = true + t.test.expectedError = ":DECODE_ERROR:" + t.test.expectedLocalError = "remote error: error decoding message" + + if t.test.config.MaxVersion >= VersionTLS13 && t.messageType == typeServerHello { + // In TLS 1.3, a bad ServerHello means the client sends + // an unencrypted alert while the server expects + // encryption, so the alert is not readable by runner. + t.test.expectedLocalError = "local error: bad record MAC" + } + + if t.messageType == typeFinished { + // Bad Finished messages read as the verify data having + // the wrong length. + t.test.expectedError = ":DIGEST_CHECK_FAILED:" + t.test.expectedLocalError = "remote error: error decrypting message" + } + + testCases = append(testCases, t.test) + } +} + +func addTLS13HandshakeTests() { + testCases = append(testCases, testCase{ + testType: clientTest, + name: "NegotiatePSKResumption-TLS13", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + NegotiatePSKResumption: true, + }, + }, + resumeSession: true, + shouldFail: true, + expectedError: ":MISSING_KEY_SHARE:", + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "MissingKeyShare-Client", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + MissingKeyShare: true, + }, + }, + shouldFail: true, + expectedError: ":MISSING_KEY_SHARE:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "MissingKeyShare-Server", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + MissingKeyShare: true, + }, + }, + shouldFail: true, + expectedError: ":MISSING_KEY_SHARE:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "DuplicateKeyShares", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + DuplicateKeyShares: true, + }, + }, + shouldFail: true, + expectedError: ":DUPLICATE_KEY_SHARE:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 4, + }, + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-OmitEarlyDataExtension", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 4, + OmitEarlyDataExtension: true, + }, + }, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-TooMuchData", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 16384 + 1, + }, + }, + shouldFail: true, + expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-Interleaved", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 4, + InterleaveEarlyData: true, + }, + }, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-EarlyDataInTLS12", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 4, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + flags: []string{"-max-version", strconv.Itoa(VersionTLS12)}, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-HRR", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 4, + }, + DefaultCurves: []CurveID{}, + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-HRR-Interleaved", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 4, + InterleaveEarlyData: true, + }, + DefaultCurves: []CurveID{}, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_RECORD:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-HRR-TooMuchData", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendFakeEarlyDataLength: 16384 + 1, + }, + DefaultCurves: []CurveID{}, + }, + shouldFail: true, + expectedError: ":TOO_MUCH_SKIPPED_EARLY_DATA:", + }) + + // Test that skipping early data looking for cleartext correctly + // processes an alert record. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-HRR-FatalAlert", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendEarlyAlert: true, + SendFakeEarlyDataLength: 4, + }, + DefaultCurves: []CurveID{}, + }, + shouldFail: true, + expectedError: ":SSLV3_ALERT_HANDSHAKE_FAILURE:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SkipEarlyData-SecondClientHelloEarlyData", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendEarlyDataOnSecondClientHello: true, + }, + DefaultCurves: []CurveID{}, + }, + shouldFail: true, + expectedLocalError: "remote error: bad record MAC", + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "EmptyEncryptedExtensions", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + EmptyEncryptedExtensions: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: error decoding message", + }) + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "EncryptedExtensionsWithKeyShare", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + EncryptedExtensionsWithKeyShare: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: unsupported extension", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SendHelloRetryRequest", + config: Config{ + MaxVersion: VersionTLS13, + // Require a HelloRetryRequest for every curve. + DefaultCurves: []CurveID{}, + }, + expectedCurveID: CurveX25519, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SendHelloRetryRequest-2", + config: Config{ + MaxVersion: VersionTLS13, + DefaultCurves: []CurveID{CurveP384}, + }, + // Although the ClientHello did not predict our preferred curve, + // we always select it whether it is predicted or not. + expectedCurveID: CurveX25519, + }) + + testCases = append(testCases, testCase{ + name: "UnknownCurve-HelloRetryRequest", + config: Config{ + MaxVersion: VersionTLS13, + // P-384 requires HelloRetryRequest in BoringSSL. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + SendHelloRetryRequestCurve: bogusCurve, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + testCases = append(testCases, testCase{ + name: "DisabledCurve-HelloRetryRequest", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveP256}, + Bugs: ProtocolBugs{ + IgnorePeerCurvePreferences: true, + }, + }, + flags: []string{"-p384-only"}, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + testCases = append(testCases, testCase{ + name: "UnnecessaryHelloRetryRequest", + config: Config{ + MaxVersion: VersionTLS13, + CurvePreferences: []CurveID{CurveX25519}, + Bugs: ProtocolBugs{ + SendHelloRetryRequestCurve: CurveX25519, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + testCases = append(testCases, testCase{ + name: "SecondHelloRetryRequest", + config: Config{ + MaxVersion: VersionTLS13, + // P-384 requires HelloRetryRequest in BoringSSL. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + SecondHelloRetryRequest: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequest-Empty", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + AlwaysSendHelloRetryRequest: true, + }, + }, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequest-DuplicateCurve", + config: Config{ + MaxVersion: VersionTLS13, + // P-384 requires a HelloRetryRequest against BoringSSL's default + // configuration. Assert this ExpectMissingKeyShare. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + ExpectMissingKeyShare: true, + DuplicateHelloRetryRequestExtensions: true, + }, + }, + shouldFail: true, + expectedError: ":DUPLICATE_EXTENSION:", + expectedLocalError: "remote error: illegal parameter", + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequest-Cookie", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendHelloRetryRequestCookie: []byte("cookie"), + }, + }, + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequest-DuplicateCookie", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendHelloRetryRequestCookie: []byte("cookie"), + DuplicateHelloRetryRequestExtensions: true, + }, + }, + shouldFail: true, + expectedError: ":DUPLICATE_EXTENSION:", + expectedLocalError: "remote error: illegal parameter", + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequest-EmptyCookie", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SendHelloRetryRequestCookie: []byte{}, + }, + }, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequest-Cookie-Curve", + config: Config{ + MaxVersion: VersionTLS13, + // P-384 requires HelloRetryRequest in BoringSSL. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + SendHelloRetryRequestCookie: []byte("cookie"), + ExpectMissingKeyShare: true, + }, + }, + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequest-Unknown", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + CustomHelloRetryRequestExtension: "extension", + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + expectedLocalError: "remote error: unsupported extension", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SecondClientHelloMissingKeyShare", + config: Config{ + MaxVersion: VersionTLS13, + DefaultCurves: []CurveID{}, + Bugs: ProtocolBugs{ + SecondClientHelloMissingKeyShare: true, + }, + }, + shouldFail: true, + expectedError: ":MISSING_KEY_SHARE:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SecondClientHelloWrongCurve", + config: Config{ + MaxVersion: VersionTLS13, + DefaultCurves: []CurveID{}, + Bugs: ProtocolBugs{ + MisinterpretHelloRetryRequestCurve: CurveP521, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequestVersionMismatch", + config: Config{ + MaxVersion: VersionTLS13, + // P-384 requires HelloRetryRequest in BoringSSL. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + SendServerHelloVersion: 0x0305, + }, + }, + shouldFail: true, + expectedError: ":WRONG_VERSION_NUMBER:", + }) + + testCases = append(testCases, testCase{ + name: "HelloRetryRequestCurveMismatch", + config: Config{ + MaxVersion: VersionTLS13, + // P-384 requires HelloRetryRequest in BoringSSL. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + // Send P-384 (correct) in the HelloRetryRequest. + SendHelloRetryRequestCurve: CurveP384, + // But send P-256 in the ServerHello. + SendCurve: CurveP256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + // Test the server selecting a curve that requires a HelloRetryRequest + // without sending it. + testCases = append(testCases, testCase{ + name: "SkipHelloRetryRequest", + config: Config{ + MaxVersion: VersionTLS13, + // P-384 requires HelloRetryRequest in BoringSSL. + CurvePreferences: []CurveID{CurveP384}, + Bugs: ProtocolBugs{ + SkipHelloRetryRequest: true, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }) + + testCases = append(testCases, testCase{ + name: "TLS13-RequestContextInHandshake", + config: Config{ + MaxVersion: VersionTLS13, + MinVersion: VersionTLS13, + ClientAuth: RequireAnyClientCert, + Bugs: ProtocolBugs{ + SendRequestContext: []byte("request context"), + }, + }, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaCertificateFile), + "-key-file", path.Join(*resourceDir, rsaKeyFile), + }, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-TrailingKeyShareData", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + TrailingKeyShareData: true, + }, + }, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }) + + testCases = append(testCases, testCase{ + name: "TLS13-AlwaysSelectPSKIdentity", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + AlwaysSelectPSKIdentity: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_EXTENSION:", + }) + + testCases = append(testCases, testCase{ + name: "TLS13-InvalidPSKIdentity", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + SelectPSKIdentityOnResume: 1, + }, + }, + resumeSession: true, + shouldFail: true, + expectedError: ":PSK_IDENTITY_NOT_FOUND:", + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-ExtraPSKIdentity", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + ExtraPSKIdentity: true, + SendExtraPSKBinder: true, + }, + }, + resumeSession: true, + }) + + // Test that unknown NewSessionTicket extensions are tolerated. + testCases = append(testCases, testCase{ + name: "TLS13-CustomTicketExtension", + config: Config{ + MaxVersion: VersionTLS13, + Bugs: ProtocolBugs{ + CustomTicketExtension: "1234", + }, + }, + }) +} + +func addTLS13CipherPreferenceTests() { + // Test that client preference is honored if the shim has AES hardware + // and ChaCha20-Poly1305 is preferred otherwise. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-CipherPreference-Server-ChaCha20-AES", + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{ + TLS_CHACHA20_POLY1305_SHA256, + TLS_AES_128_GCM_SHA256, + }, + }, + flags: []string{ + "-expect-cipher-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)), + "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)), + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "TLS13-CipherPreference-Server-AES-ChaCha20", + config: Config{ + MaxVersion: VersionTLS13, + CipherSuites: []uint16{ + TLS_AES_128_GCM_SHA256, + TLS_CHACHA20_POLY1305_SHA256, + }, + }, + flags: []string{ + "-expect-cipher-aes", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)), + "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)), + }, + }) + + // Test that the client orders ChaCha20-Poly1305 and AES-GCM based on + // whether it has AES hardware. + testCases = append(testCases, testCase{ + name: "TLS13-CipherPreference-Client", + config: Config{ + MaxVersion: VersionTLS13, + // Use the client cipher order. (This is the default but + // is listed to be explicit.) + PreferServerCipherSuites: false, + }, + flags: []string{ + "-expect-cipher-aes", strconv.Itoa(int(TLS_AES_128_GCM_SHA256)), + "-expect-cipher-no-aes", strconv.Itoa(int(TLS_CHACHA20_POLY1305_SHA256)), + }, + }) +} + +func addPeekTests() { + // Test SSL_peek works, including on empty records. + testCases = append(testCases, testCase{ + name: "Peek-Basic", + sendEmptyRecords: 1, + flags: []string{"-peek-then-read"}, + }) + + // Test SSL_peek can drive the initial handshake. + testCases = append(testCases, testCase{ + name: "Peek-ImplicitHandshake", + flags: []string{ + "-peek-then-read", + "-implicit-handshake", + }, + }) + + // Test SSL_peek can discover and drive a renegotiation. + testCases = append(testCases, testCase{ + name: "Peek-Renegotiate", + config: Config{ + MaxVersion: VersionTLS12, + }, + renegotiate: 1, + flags: []string{ + "-peek-then-read", + "-renegotiate-freely", + "-expect-total-renegotiations", "1", + }, + }) + + // Test SSL_peek can discover a close_notify. + testCases = append(testCases, testCase{ + name: "Peek-Shutdown", + config: Config{ + Bugs: ProtocolBugs{ + ExpectCloseNotify: true, + }, + }, + flags: []string{ + "-peek-then-read", + "-check-close-notify", + }, + }) + + // Test SSL_peek can discover an alert. + testCases = append(testCases, testCase{ + name: "Peek-Alert", + config: Config{ + Bugs: ProtocolBugs{ + SendSpuriousAlert: alertRecordOverflow, + }, + }, + flags: []string{"-peek-then-read"}, + shouldFail: true, + expectedError: ":TLSV1_ALERT_RECORD_OVERFLOW:", + }) + + // Test SSL_peek can handle KeyUpdate. + testCases = append(testCases, testCase{ + name: "Peek-KeyUpdate", + config: Config{ + MaxVersion: VersionTLS13, + }, + sendKeyUpdates: 1, + keyUpdateRequest: keyUpdateNotRequested, + flags: []string{"-peek-then-read"}, + }) +} + +func addRecordVersionTests() { + for _, ver := range tlsVersions { + // Test that the record version is enforced. + testCases = append(testCases, testCase{ + name: "CheckRecordVersion-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SendRecordVersion: 0x03ff, + }, + }, + shouldFail: true, + expectedError: ":WRONG_VERSION_NUMBER:", + }) + + // Test that the ClientHello may use any record version, for + // compatibility reasons. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "LooseInitialRecordVersion-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SendInitialRecordVersion: 0x03ff, + }, + }, + }) + + // Test that garbage ClientHello record versions are rejected. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "GarbageInitialRecordVersion-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Bugs: ProtocolBugs{ + SendInitialRecordVersion: 0xffff, + }, + }, + shouldFail: true, + expectedError: ":WRONG_VERSION_NUMBER:", + }) + } +} + +func addCertificateTests() { + // Test that a certificate chain with intermediate may be sent and + // received as both client and server. + for _, ver := range tlsVersions { + testCases = append(testCases, testCase{ + testType: clientTest, + name: "SendReceiveIntermediate-Client-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{rsaChainCertificate}, + ClientAuth: RequireAnyClientCert, + }, + expectPeerCertificate: &rsaChainCertificate, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile), + "-key-file", path.Join(*resourceDir, rsaChainKeyFile), + "-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile), + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SendReceiveIntermediate-Server-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{rsaChainCertificate}, + }, + expectPeerCertificate: &rsaChainCertificate, + flags: []string{ + "-cert-file", path.Join(*resourceDir, rsaChainCertificateFile), + "-key-file", path.Join(*resourceDir, rsaChainKeyFile), + "-require-any-client-certificate", + "-expect-peer-cert-file", path.Join(*resourceDir, rsaChainCertificateFile), + }, + }) + } +} + +func addRetainOnlySHA256ClientCertTests() { + for _, ver := range tlsVersions { + // Test that enabling + // SSL_CTX_set_retain_only_sha256_of_client_certs without + // actually requesting a client certificate is a no-op. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "RetainOnlySHA256-NoCert-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + }, + flags: []string{ + "-retain-only-sha256-client-cert-initial", + "-retain-only-sha256-client-cert-resume", + }, + resumeSession: true, + }) + + // Test that when retaining only a SHA-256 certificate is + // enabled, the hash appears as expected. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "RetainOnlySHA256-Cert-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{ + "-verify-peer", + "-retain-only-sha256-client-cert-initial", + "-retain-only-sha256-client-cert-resume", + "-expect-sha256-client-cert-initial", + "-expect-sha256-client-cert-resume", + }, + resumeSession: true, + }) + + // Test that when the config changes from on to off, a + // resumption is rejected because the server now wants the full + // certificate chain. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "RetainOnlySHA256-OnOff-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{ + "-verify-peer", + "-retain-only-sha256-client-cert-initial", + "-expect-sha256-client-cert-initial", + }, + resumeSession: true, + expectResumeRejected: true, + }) + + // Test that when the config changes from off to on, a + // resumption is rejected because the server now wants just the + // hash. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "RetainOnlySHA256-OffOn-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{rsaCertificate}, + }, + flags: []string{ + "-verify-peer", + "-retain-only-sha256-client-cert-resume", + "-expect-sha256-client-cert-resume", + }, + resumeSession: true, + expectResumeRejected: true, + }) + } +} + +func addECDSAKeyUsageTests() { + p256 := elliptic.P256() + priv, err := ecdsa.GenerateKey(p256, rand.Reader) + if err != nil { + panic(err) + } + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) + if err != nil { + panic(err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now(), + + // An ECC certificate with only the keyAgreement key usgae may + // be used with ECDH, but not ECDSA. + KeyUsage: x509.KeyUsageKeyAgreement, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + panic(err) + } + + cert := Certificate{ + Certificate: [][]byte{derBytes}, + PrivateKey: priv, + } + + for _, ver := range tlsVersions { + if ver.version < VersionTLS12 { + continue + } + + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ECDSAKeyUsage-" + ver.name, + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + Certificates: []Certificate{cert}, + }, + shouldFail: true, + expectedError: ":ECC_CERT_NOT_FOR_SIGNING:", + }) + } +} + +func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) { + defer wg.Done() + + for test := range c { + var err error + + if *mallocTest >= 0 { + for mallocNumToFail := int64(*mallocTest); ; mallocNumToFail++ { + statusChan <- statusMsg{test: test, started: true} + if err = runTest(test, shimPath, mallocNumToFail); err != errMoreMallocs { + if err != nil { + fmt.Printf("\n\nmalloc test failed at %d: %s\n", mallocNumToFail, err) + } + break + } + } + } else if *repeatUntilFailure { + for err == nil { + statusChan <- statusMsg{test: test, started: true} + err = runTest(test, shimPath, -1) + } + } else { + statusChan <- statusMsg{test: test, started: true} + err = runTest(test, shimPath, -1) + } + statusChan <- statusMsg{test: test, err: err} + } +} + +type statusMsg struct { + test *testCase + started bool + err error +} + +func statusPrinter(doneChan chan *testOutput, statusChan chan statusMsg, total int) { + var started, done, failed, unimplemented, lineLen int + + testOutput := newTestOutput() + for msg := range statusChan { + if !*pipe { + // Erase the previous status line. + var erase string + for i := 0; i < lineLen; i++ { + erase += "\b \b" + } + fmt.Print(erase) + } + + if msg.started { + started++ + } else { + done++ + + if msg.err != nil { + if msg.err == errUnimplemented { + if *pipe { + // Print each test instead of a status line. + fmt.Printf("UNIMPLEMENTED (%s)\n", msg.test.name) + } + unimplemented++ + testOutput.addResult(msg.test.name, "UNIMPLEMENTED") + } else { + fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err) + failed++ + testOutput.addResult(msg.test.name, "FAIL") + } + } else { + if *pipe { + // Print each test instead of a status line. + fmt.Printf("PASSED (%s)\n", msg.test.name) + } + testOutput.addResult(msg.test.name, "PASS") + } + } + + if !*pipe { + // Print a new status line. + line := fmt.Sprintf("%d/%d/%d/%d/%d", failed, unimplemented, done, started, total) + lineLen = len(line) + os.Stdout.WriteString(line) + } + } + + doneChan <- testOutput +} + +func main() { + flag.Parse() + *resourceDir = path.Clean(*resourceDir) + initCertificates() + + addBasicTests() + addCipherSuiteTests() + addBadECDSASignatureTests() + addCBCPaddingTests() + addCBCSplittingTests() + addClientAuthTests() + addDDoSCallbackTests() + addVersionNegotiationTests() + addMinimumVersionTests() + addExtensionTests() + addResumptionVersionTests() + addExtendedMasterSecretTests() + addRenegotiationTests() + addDTLSReplayTests() + addSignatureAlgorithmTests() + addDTLSRetransmitTests() + addExportKeyingMaterialTests() + addTLSUniqueTests() + addCustomExtensionTests() + addRSAClientKeyExchangeTests() + addCurveTests() + addSessionTicketTests() + addTLS13RecordTests() + addAllStateMachineCoverageTests() + addChangeCipherSpecTests() + addWrongMessageTypeTests() + addTrailingMessageDataTests() + addTLS13HandshakeTests() + addTLS13CipherPreferenceTests() + addPeekTests() + addRecordVersionTests() + addCertificateTests() + addRetainOnlySHA256ClientCertTests() + addECDSAKeyUsageTests() + + var wg sync.WaitGroup + + statusChan := make(chan statusMsg, *numWorkers) + testChan := make(chan *testCase, *numWorkers) + doneChan := make(chan *testOutput) + + if len(*shimConfigFile) != 0 { + encoded, err := ioutil.ReadFile(*shimConfigFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't read config file %q: %s\n", *shimConfigFile, err) + os.Exit(1) + } + + if err := json.Unmarshal(encoded, &shimConfig); err != nil { + fmt.Fprintf(os.Stderr, "Couldn't decode config file %q: %s\n", *shimConfigFile, err) + os.Exit(1) + } + } + + go statusPrinter(doneChan, statusChan, len(testCases)) + + for i := 0; i < *numWorkers; i++ { + wg.Add(1) + go worker(statusChan, testChan, *shimPath, &wg) + } + + var foundTest bool + for i := range testCases { + matched := true + if len(*testToRun) != 0 { + var err error + matched, err = filepath.Match(*testToRun, testCases[i].name) + if err != nil { + fmt.Fprintf(os.Stderr, "Error matching pattern: %s\n", err) + os.Exit(1) + } + } + + if !*includeDisabled { + for pattern := range shimConfig.DisabledTests { + isDisabled, err := filepath.Match(pattern, testCases[i].name) + if err != nil { + fmt.Fprintf(os.Stderr, "Error matching pattern %q from config file: %s\n", pattern, err) + os.Exit(1) + } + + if isDisabled { + matched = false + break + } + } + } + + if matched { + foundTest = true + testChan <- &testCases[i] + + // Only run one test if repeating until failure. + if *repeatUntilFailure { + break + } + } + } + + if !foundTest { + fmt.Fprintf(os.Stderr, "No tests run\n") + os.Exit(1) + } + + close(testChan) + wg.Wait() + close(statusChan) + testOutput := <-doneChan + + fmt.Printf("\n") + + if *jsonOutput != "" { + if err := testOutput.writeTo(*jsonOutput); err != nil { + fmt.Fprintf(os.Stderr, "Error: %s\n", err) + } + } + + if !*allowUnimplemented && testOutput.NumFailuresByType["UNIMPLEMENTED"] > 0 { + os.Exit(1) + } + + if !testOutput.noneFailed { + os.Exit(1) + } +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/runner_test.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/runner_test.go new file mode 100644 index 000000000..6c7fcb0c9 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/runner_test.go @@ -0,0 +1,21 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import "testing" + +func TestAll(t *testing.T) { + main() +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/shim_ticket.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/shim_ticket.go new file mode 100644 index 000000000..9e57d482a --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/shim_ticket.go @@ -0,0 +1,249 @@ +// Copyright (c) 2016, Google Inc. +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package runner + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "encoding/asn1" + "errors" +) + +// TestShimTicketKey is the testing key assumed for the shim. +var TestShimTicketKey = make([]byte, 48) + +func DecryptShimTicket(in []byte) ([]byte, error) { + name := TestShimTicketKey[:16] + macKey := TestShimTicketKey[16:32] + encKey := TestShimTicketKey[32:48] + + h := hmac.New(sha256.New, macKey) + + block, err := aes.NewCipher(encKey) + if err != nil { + panic(err) + } + + if len(in) < len(name)+block.BlockSize()+1+h.Size() { + return nil, errors.New("tls: shim ticket too short") + } + + // Check the key name. + if !bytes.Equal(name, in[:len(name)]) { + return nil, errors.New("tls: shim ticket name mismatch") + } + + // Check the MAC at the end of the ticket. + mac := in[len(in)-h.Size():] + in = in[:len(in)-h.Size()] + h.Write(in) + if !hmac.Equal(mac, h.Sum(nil)) { + return nil, errors.New("tls: shim ticket MAC mismatch") + } + + // The MAC covers the key name, but the encryption does not. + in = in[len(name):] + + // Decrypt in-place. + iv := in[:block.BlockSize()] + in = in[block.BlockSize():] + if l := len(in); l == 0 || l%block.BlockSize() != 0 { + return nil, errors.New("tls: ticket ciphertext not a multiple of the block size") + } + out := make([]byte, len(in)) + cbc := cipher.NewCBCDecrypter(block, iv) + cbc.CryptBlocks(out, in) + + // Remove the padding. + pad := int(out[len(out)-1]) + if pad == 0 || pad > block.BlockSize() || pad > len(in) { + return nil, errors.New("tls: bad shim ticket CBC pad") + } + + for i := 0; i < pad; i++ { + if out[len(out)-1-i] != byte(pad) { + return nil, errors.New("tls: bad shim ticket CBC pad") + } + } + + return out[:len(out)-pad], nil +} + +func EncryptShimTicket(in []byte) []byte { + name := TestShimTicketKey[:16] + macKey := TestShimTicketKey[16:32] + encKey := TestShimTicketKey[32:48] + + h := hmac.New(sha256.New, macKey) + + block, err := aes.NewCipher(encKey) + if err != nil { + panic(err) + } + + // Use the zero IV for rewritten tickets. + iv := make([]byte, block.BlockSize()) + cbc := cipher.NewCBCEncrypter(block, iv) + pad := block.BlockSize() - (len(in) % block.BlockSize()) + + out := make([]byte, 0, len(name)+len(iv)+len(in)+pad+h.Size()) + out = append(out, name...) + out = append(out, iv...) + out = append(out, in...) + for i := 0; i < pad; i++ { + out = append(out, byte(pad)) + } + + ciphertext := out[len(name)+len(iv):] + cbc.CryptBlocks(ciphertext, ciphertext) + + h.Write(out) + return h.Sum(out) +} + +const asn1Constructed = 0x20 + +func parseDERElement(in []byte) (tag byte, body, rest []byte, ok bool) { + rest = in + if len(rest) < 1 { + return + } + + tag = rest[0] + rest = rest[1:] + + if tag&0x1f == 0x1f { + // Long-form tags not supported. + return + } + + if len(rest) < 1 { + return + } + + length := int(rest[0]) + rest = rest[1:] + if length > 0x7f { + lengthLength := length & 0x7f + length = 0 + if lengthLength == 0 { + // No indefinite-length encoding. + return + } + + // Decode long-form lengths. + for lengthLength > 0 { + if len(rest) < 1 || (length<<8)>>8 != length { + return + } + if length == 0 && rest[0] == 0 { + // Length not minimally-encoded. + return + } + length <<= 8 + length |= int(rest[0]) + rest = rest[1:] + lengthLength-- + } + + if length < 0x80 { + // Length not minimally-encoded. + return + } + } + + if len(rest) < length { + return + } + + body = rest[:length] + rest = rest[length:] + ok = true + return +} + +func SetShimTicketVersion(in []byte, vers uint16) ([]byte, error) { + plaintext, err := DecryptShimTicket(in) + if err != nil { + return nil, err + } + + tag, session, _, ok := parseDERElement(plaintext) + if !ok || tag != asn1.TagSequence|asn1Constructed { + return nil, errors.New("tls: could not decode shim session") + } + + // Skip the session version. + tag, _, session, ok = parseDERElement(session) + if !ok || tag != asn1.TagInteger { + return nil, errors.New("tls: could not decode shim session") + } + + // Next field is the protocol version. + tag, version, _, ok := parseDERElement(session) + if !ok || tag != asn1.TagInteger { + return nil, errors.New("tls: could not decode shim session") + } + + // This code assumes both old and new versions are encoded in two + // bytes. This isn't quite right as INTEGERs are minimally-encoded, but + // we do not need to support other caess for now. + if len(version) != 2 || vers < 0x80 || vers >= 0x8000 { + return nil, errors.New("tls: unsupported version in shim session") + } + + version[0] = byte(vers >> 8) + version[1] = byte(vers) + + return EncryptShimTicket(plaintext), nil +} + +func SetShimTicketCipherSuite(in []byte, id uint16) ([]byte, error) { + plaintext, err := DecryptShimTicket(in) + if err != nil { + return nil, err + } + + tag, session, _, ok := parseDERElement(plaintext) + if !ok || tag != asn1.TagSequence|asn1Constructed { + return nil, errors.New("tls: could not decode shim session") + } + + // Skip the session version. + tag, _, session, ok = parseDERElement(session) + if !ok || tag != asn1.TagInteger { + return nil, errors.New("tls: could not decode shim session") + } + + // Skip the protocol version. + tag, _, session, ok = parseDERElement(session) + if !ok || tag != asn1.TagInteger { + return nil, errors.New("tls: could not decode shim session") + } + + // Next field is the cipher suite. + tag, cipherSuite, _, ok := parseDERElement(session) + if !ok || tag != asn1.TagOctetString || len(cipherSuite) != 2 { + return nil, errors.New("tls: could not decode shim session") + } + + cipherSuite[0] = byte(id >> 8) + cipherSuite[1] = byte(id) + + return EncryptShimTicket(plaintext), nil +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/sign.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/sign.go new file mode 100644 index 000000000..5f56ff960 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/sign.go @@ -0,0 +1,297 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/md5" + "crypto/rsa" + "crypto/sha1" + _ "crypto/sha256" + _ "crypto/sha512" + "encoding/asn1" + "errors" + "fmt" + "math/big" +) + +type signer interface { + supportsKey(key crypto.PrivateKey) bool + signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) + verifyMessage(key crypto.PublicKey, msg, sig []byte) error +} + +func selectSignatureAlgorithm(version uint16, key crypto.PrivateKey, config *Config, peerSigAlgs []signatureAlgorithm) (signatureAlgorithm, error) { + // If the client didn't specify any signature_algorithms extension then + // we can assume that it supports SHA1. See + // http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + if len(peerSigAlgs) == 0 { + peerSigAlgs = []signatureAlgorithm{signatureRSAPKCS1WithSHA1, signatureECDSAWithSHA1} + } + + for _, sigAlg := range config.signSignatureAlgorithms() { + if !isSupportedSignatureAlgorithm(sigAlg, peerSigAlgs) { + continue + } + + signer, err := getSigner(version, key, config, sigAlg) + if err != nil { + continue + } + + if signer.supportsKey(key) { + return sigAlg, nil + } + } + return 0, errors.New("tls: no common signature algorithms") +} + +func signMessage(version uint16, key crypto.PrivateKey, config *Config, sigAlg signatureAlgorithm, msg []byte) ([]byte, error) { + if config.Bugs.InvalidSignature { + newMsg := make([]byte, len(msg)) + copy(newMsg, msg) + newMsg[0] ^= 0x80 + msg = newMsg + } + + signer, err := getSigner(version, key, config, sigAlg) + if err != nil { + return nil, err + } + + return signer.signMessage(key, config, msg) +} + +func verifyMessage(version uint16, key crypto.PublicKey, config *Config, sigAlg signatureAlgorithm, msg, sig []byte) error { + if version >= VersionTLS12 && !isSupportedSignatureAlgorithm(sigAlg, config.verifySignatureAlgorithms()) { + return errors.New("tls: unsupported signature algorithm") + } + + signer, err := getSigner(version, key, config, sigAlg) + if err != nil { + return err + } + + return signer.verifyMessage(key, msg, sig) +} + +type rsaPKCS1Signer struct { + hash crypto.Hash +} + +func (r *rsaPKCS1Signer) computeHash(msg []byte) []byte { + if r.hash == crypto.MD5SHA1 { + // crypto.MD5SHA1 is not a real hash function. + hashMD5 := md5.New() + hashMD5.Write(msg) + hashSHA1 := sha1.New() + hashSHA1.Write(msg) + return hashSHA1.Sum(hashMD5.Sum(nil)) + } + + h := r.hash.New() + h.Write(msg) + return h.Sum(nil) +} + +func (r *rsaPKCS1Signer) supportsKey(key crypto.PrivateKey) bool { + _, ok := key.(*rsa.PrivateKey) + return ok +} + +func (r *rsaPKCS1Signer) signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) { + rsaKey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("invalid key type for RSA-PKCS1") + } + + return rsa.SignPKCS1v15(config.rand(), rsaKey, r.hash, r.computeHash(msg)) +} + +func (r *rsaPKCS1Signer) verifyMessage(key crypto.PublicKey, msg, sig []byte) error { + rsaKey, ok := key.(*rsa.PublicKey) + if !ok { + return errors.New("invalid key type for RSA-PKCS1") + } + + return rsa.VerifyPKCS1v15(rsaKey, r.hash, r.computeHash(msg), sig) +} + +type ecdsaSigner struct { + version uint16 + config *Config + curve elliptic.Curve + hash crypto.Hash +} + +func (e *ecdsaSigner) isCurveValid(curve elliptic.Curve) bool { + if e.config.Bugs.SkipECDSACurveCheck { + return true + } + if e.version <= VersionTLS12 { + return true + } + return e.curve != nil && curve == e.curve +} + +func (e *ecdsaSigner) supportsKey(key crypto.PrivateKey) bool { + ecdsaKey, ok := key.(*ecdsa.PrivateKey) + return ok && e.isCurveValid(ecdsaKey.Curve) +} + +func maybeCorruptECDSAValue(n *big.Int, typeOfCorruption BadValue, limit *big.Int) *big.Int { + switch typeOfCorruption { + case BadValueNone: + return n + case BadValueNegative: + return new(big.Int).Neg(n) + case BadValueZero: + return big.NewInt(0) + case BadValueLimit: + return limit + case BadValueLarge: + bad := new(big.Int).Set(limit) + return bad.Lsh(bad, 20) + default: + panic("unknown BadValue type") + } +} + +func (e *ecdsaSigner) signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) { + ecdsaKey, ok := key.(*ecdsa.PrivateKey) + if !ok { + return nil, errors.New("invalid key type for ECDSA") + } + if !e.isCurveValid(ecdsaKey.Curve) { + return nil, errors.New("invalid curve for ECDSA") + } + + h := e.hash.New() + h.Write(msg) + digest := h.Sum(nil) + + r, s, err := ecdsa.Sign(config.rand(), ecdsaKey, digest) + if err != nil { + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + } + order := ecdsaKey.Curve.Params().N + r = maybeCorruptECDSAValue(r, config.Bugs.BadECDSAR, order) + s = maybeCorruptECDSAValue(s, config.Bugs.BadECDSAS, order) + return asn1.Marshal(ecdsaSignature{r, s}) +} + +func (e *ecdsaSigner) verifyMessage(key crypto.PublicKey, msg, sig []byte) error { + ecdsaKey, ok := key.(*ecdsa.PublicKey) + if !ok { + return errors.New("invalid key type for ECDSA") + } + if !e.isCurveValid(ecdsaKey.Curve) { + return errors.New("invalid curve for ECDSA") + } + + ecdsaSig := new(ecdsaSignature) + if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("ECDSA signature contained zero or negative values") + } + + h := e.hash.New() + h.Write(msg) + if !ecdsa.Verify(ecdsaKey, h.Sum(nil), ecdsaSig.R, ecdsaSig.S) { + return errors.New("ECDSA verification failure") + } + return nil +} + +var pssOptions = rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash} + +type rsaPSSSigner struct { + hash crypto.Hash +} + +func (r *rsaPSSSigner) supportsKey(key crypto.PrivateKey) bool { + _, ok := key.(*rsa.PrivateKey) + return ok +} + +func (r *rsaPSSSigner) signMessage(key crypto.PrivateKey, config *Config, msg []byte) ([]byte, error) { + rsaKey, ok := key.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("invalid key type for RSA-PSS") + } + + h := r.hash.New() + h.Write(msg) + return rsa.SignPSS(config.rand(), rsaKey, r.hash, h.Sum(nil), &pssOptions) +} + +func (r *rsaPSSSigner) verifyMessage(key crypto.PublicKey, msg, sig []byte) error { + rsaKey, ok := key.(*rsa.PublicKey) + if !ok { + return errors.New("invalid key type for RSA-PSS") + } + + h := r.hash.New() + h.Write(msg) + return rsa.VerifyPSS(rsaKey, r.hash, h.Sum(nil), sig, &pssOptions) +} + +func getSigner(version uint16, key interface{}, config *Config, sigAlg signatureAlgorithm) (signer, error) { + // TLS 1.1 and below use legacy signature algorithms. + if version < VersionTLS12 { + switch key.(type) { + case *rsa.PrivateKey, *rsa.PublicKey: + return &rsaPKCS1Signer{crypto.MD5SHA1}, nil + case *ecdsa.PrivateKey, *ecdsa.PublicKey: + return &ecdsaSigner{version, config, nil, crypto.SHA1}, nil + default: + return nil, errors.New("unknown key type") + } + } + + // TODO(davidben): Forbid RSASSA-PKCS1-v1_5 in TLS 1.3. + switch sigAlg { + case signatureRSAPKCS1WithMD5: + if version < VersionTLS13 || config.Bugs.IgnoreSignatureVersionChecks { + return &rsaPKCS1Signer{crypto.MD5}, nil + } + case signatureRSAPKCS1WithSHA1: + if version < VersionTLS13 || config.Bugs.IgnoreSignatureVersionChecks { + return &rsaPKCS1Signer{crypto.SHA1}, nil + } + case signatureRSAPKCS1WithSHA256: + if version < VersionTLS13 || config.Bugs.IgnoreSignatureVersionChecks { + return &rsaPKCS1Signer{crypto.SHA256}, nil + } + case signatureRSAPKCS1WithSHA384: + if version < VersionTLS13 || config.Bugs.IgnoreSignatureVersionChecks { + return &rsaPKCS1Signer{crypto.SHA384}, nil + } + case signatureRSAPKCS1WithSHA512: + if version < VersionTLS13 || config.Bugs.IgnoreSignatureVersionChecks { + return &rsaPKCS1Signer{crypto.SHA512}, nil + } + case signatureECDSAWithSHA1: + return &ecdsaSigner{version, config, nil, crypto.SHA1}, nil + case signatureECDSAWithP256AndSHA256: + return &ecdsaSigner{version, config, elliptic.P256(), crypto.SHA256}, nil + case signatureECDSAWithP384AndSHA384: + return &ecdsaSigner{version, config, elliptic.P384(), crypto.SHA384}, nil + case signatureECDSAWithP521AndSHA512: + return &ecdsaSigner{version, config, elliptic.P521(), crypto.SHA512}, nil + case signatureRSAPSSWithSHA256: + return &rsaPSSSigner{crypto.SHA256}, nil + case signatureRSAPSSWithSHA384: + return &rsaPSSSigner{crypto.SHA384}, nil + case signatureRSAPSSWithSHA512: + return &rsaPSSSigner{crypto.SHA512}, nil + } + + return nil, fmt.Errorf("unsupported signature algorithm %04x", sigAlg) +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/test_output.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/test_output.go new file mode 100644 index 000000000..eb5463824 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/test_output.go @@ -0,0 +1,79 @@ +/* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +package runner + +import ( + "encoding/json" + "os" + "time" +) + +// testOutput is a representation of Chromium's JSON test result format. See +// https://www.chromium.org/developers/the-json-test-results-format +type testOutput struct { + Version int `json:"version"` + Interrupted bool `json:"interrupted"` + PathDelimiter string `json:"path_delimiter"` + SecondsSinceEpoch float64 `json:"seconds_since_epoch"` + NumFailuresByType map[string]int `json:"num_failures_by_type"` + Tests map[string]testResult `json:"tests"` + noneFailed bool +} + +type testResult struct { + Actual string `json:"actual"` + Expected string `json:"expected"` + IsUnexpected bool `json:"is_unexpected"` +} + +func newTestOutput() *testOutput { + return &testOutput{ + Version: 3, + PathDelimiter: ".", + SecondsSinceEpoch: float64(time.Now().UnixNano()) / float64(time.Second/time.Nanosecond), + NumFailuresByType: make(map[string]int), + Tests: make(map[string]testResult), + noneFailed: true, + } +} + +func (t *testOutput) addResult(name, result string) { + if _, found := t.Tests[name]; found { + panic(name) + } + t.Tests[name] = testResult{ + Actual: result, + Expected: "PASS", + IsUnexpected: result != "PASS", + } + t.NumFailuresByType[result]++ + if result != "PASS" && result != "UNIMPLEMENTED" { + t.noneFailed = false + } +} + +func (t *testOutput) writeTo(name string) error { + file, err := os.Create(name) + if err != nil { + return err + } + defer file.Close() + out, err := json.MarshalIndent(t, "", " ") + if err != nil { + return err + } + _, err = file.Write(out) + return err +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ticket.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ticket.go new file mode 100644 index 000000000..4a4540c5d --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/ticket.go @@ -0,0 +1,197 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runner + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "crypto/subtle" + "encoding/binary" + "errors" + "io" + "time" +) + +// sessionState contains the information that is serialized into a session +// ticket in order to later resume a connection. +type sessionState struct { + vers uint16 + cipherSuite uint16 + masterSecret []byte + handshakeHash []byte + certificates [][]byte + extendedMasterSecret bool + ticketCreationTime time.Time + ticketExpiration time.Time + ticketFlags uint32 + ticketAgeAdd uint32 +} + +func (s *sessionState) marshal() []byte { + msg := newByteBuilder() + msg.addU16(s.vers) + msg.addU16(s.cipherSuite) + masterSecret := msg.addU16LengthPrefixed() + masterSecret.addBytes(s.masterSecret) + handshakeHash := msg.addU16LengthPrefixed() + handshakeHash.addBytes(s.handshakeHash) + msg.addU16(uint16(len(s.certificates))) + for _, cert := range s.certificates { + certMsg := msg.addU32LengthPrefixed() + certMsg.addBytes(cert) + } + + if s.extendedMasterSecret { + msg.addU8(1) + } else { + msg.addU8(0) + } + + if s.vers >= VersionTLS13 { + msg.addU64(uint64(s.ticketCreationTime.UnixNano())) + msg.addU64(uint64(s.ticketExpiration.UnixNano())) + msg.addU32(s.ticketFlags) + msg.addU32(s.ticketAgeAdd) + } + + return msg.finish() +} + +func (s *sessionState) unmarshal(data []byte) bool { + if len(data) < 8 { + return false + } + + s.vers = uint16(data[0])<<8 | uint16(data[1]) + s.cipherSuite = uint16(data[2])<<8 | uint16(data[3]) + masterSecretLen := int(data[4])<<8 | int(data[5]) + data = data[6:] + if len(data) < masterSecretLen { + return false + } + + s.masterSecret = data[:masterSecretLen] + data = data[masterSecretLen:] + + if len(data) < 2 { + return false + } + + handshakeHashLen := int(data[0])<<8 | int(data[1]) + data = data[2:] + if len(data) < handshakeHashLen { + return false + } + + s.handshakeHash = data[:handshakeHashLen] + data = data[handshakeHashLen:] + + if len(data) < 2 { + return false + } + + numCerts := int(data[0])<<8 | int(data[1]) + data = data[2:] + + s.certificates = make([][]byte, numCerts) + for i := range s.certificates { + if len(data) < 4 { + return false + } + certLen := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3]) + data = data[4:] + if certLen < 0 { + return false + } + if len(data) < certLen { + return false + } + s.certificates[i] = data[:certLen] + data = data[certLen:] + } + + if len(data) < 1 { + return false + } + + s.extendedMasterSecret = false + if data[0] == 1 { + s.extendedMasterSecret = true + } + data = data[1:] + + if s.vers >= VersionTLS13 { + if len(data) < 24 { + return false + } + s.ticketCreationTime = time.Unix(0, int64(binary.BigEndian.Uint64(data))) + data = data[8:] + s.ticketExpiration = time.Unix(0, int64(binary.BigEndian.Uint64(data))) + data = data[8:] + s.ticketFlags = binary.BigEndian.Uint32(data) + data = data[4:] + s.ticketAgeAdd = binary.BigEndian.Uint32(data) + data = data[4:] + } + + if len(data) > 0 { + return false + } + + return true +} + +func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) { + serialized := state.marshal() + encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size) + iv := encrypted[:aes.BlockSize] + macBytes := encrypted[len(encrypted)-sha256.Size:] + + if _, err := io.ReadFull(c.config.rand(), iv); err != nil { + return nil, err + } + block, err := aes.NewCipher(c.config.SessionTicketKey[:16]) + if err != nil { + return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error()) + } + cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized) + + mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32]) + mac.Write(encrypted[:len(encrypted)-sha256.Size]) + mac.Sum(macBytes[:0]) + + return encrypted, nil +} + +func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) { + if len(encrypted) < aes.BlockSize+sha256.Size { + return nil, false + } + + iv := encrypted[:aes.BlockSize] + macBytes := encrypted[len(encrypted)-sha256.Size:] + + mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32]) + mac.Write(encrypted[:len(encrypted)-sha256.Size]) + expected := mac.Sum(nil) + + if subtle.ConstantTimeCompare(macBytes, expected) != 1 { + return nil, false + } + + block, err := aes.NewCipher(c.config.SessionTicketKey[:16]) + if err != nil { + return nil, false + } + ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size] + plaintext := make([]byte, len(ciphertext)) + cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext) + + state := new(sessionState) + ok := state.unmarshal(plaintext) + return state, ok +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/tls.go b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/tls.go new file mode 100644 index 000000000..24f9b1ec8 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/runner/tls.go @@ -0,0 +1,279 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tls partially implements TLS 1.2, as specified in RFC 5246. +package runner + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "io/ioutil" + "net" + "strings" + "time" +) + +// Server returns a new TLS server side connection +// using conn as the underlying transport. +// The configuration config must be non-nil and must have +// at least one certificate. +func Server(conn net.Conn, config *Config) *Conn { + c := &Conn{conn: conn, config: config} + c.init() + return c +} + +// Client returns a new TLS client side connection +// using conn as the underlying transport. +// The config cannot be nil: users must set either ServerHostname or +// InsecureSkipVerify in the config. +func Client(conn net.Conn, config *Config) *Conn { + c := &Conn{conn: conn, config: config, isClient: true} + c.init() + return c +} + +// A listener implements a network listener (net.Listener) for TLS connections. +type listener struct { + net.Listener + config *Config +} + +// Accept waits for and returns the next incoming TLS connection. +// The returned connection c is a *tls.Conn. +func (l *listener) Accept() (c net.Conn, err error) { + c, err = l.Listener.Accept() + if err != nil { + return + } + c = Server(c, l.config) + return +} + +// NewListener creates a Listener which accepts connections from an inner +// Listener and wraps each connection with Server. +// The configuration config must be non-nil and must have +// at least one certificate. +func NewListener(inner net.Listener, config *Config) net.Listener { + l := new(listener) + l.Listener = inner + l.config = config + return l +} + +// Listen creates a TLS listener accepting connections on the +// given network address using net.Listen. +// The configuration config must be non-nil and must have +// at least one certificate. +func Listen(network, laddr string, config *Config) (net.Listener, error) { + if config == nil || len(config.Certificates) == 0 { + return nil, errors.New("tls.Listen: no certificates in configuration") + } + l, err := net.Listen(network, laddr) + if err != nil { + return nil, err + } + return NewListener(l, config), nil +} + +type timeoutError struct{} + +func (timeoutError) Error() string { return "tls: DialWithDialer timed out" } +func (timeoutError) Timeout() bool { return true } +func (timeoutError) Temporary() bool { return true } + +// DialWithDialer connects to the given network address using dialer.Dial and +// then initiates a TLS handshake, returning the resulting TLS connection. Any +// timeout or deadline given in the dialer apply to connection and TLS +// handshake as a whole. +// +// DialWithDialer interprets a nil configuration as equivalent to the zero +// configuration; see the documentation of Config for the defaults. +func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) { + // We want the Timeout and Deadline values from dialer to cover the + // whole process: TCP connection and TLS handshake. This means that we + // also need to start our own timers now. + timeout := dialer.Timeout + + if !dialer.Deadline.IsZero() { + deadlineTimeout := dialer.Deadline.Sub(time.Now()) + if timeout == 0 || deadlineTimeout < timeout { + timeout = deadlineTimeout + } + } + + var errChannel chan error + + if timeout != 0 { + errChannel = make(chan error, 2) + time.AfterFunc(timeout, func() { + errChannel <- timeoutError{} + }) + } + + rawConn, err := dialer.Dial(network, addr) + if err != nil { + return nil, err + } + + colonPos := strings.LastIndex(addr, ":") + if colonPos == -1 { + colonPos = len(addr) + } + hostname := addr[:colonPos] + + if config == nil { + config = defaultConfig() + } + // If no ServerName is set, infer the ServerName + // from the hostname we're connecting to. + if config.ServerName == "" { + // Make a copy to avoid polluting argument or default. + c := *config + c.ServerName = hostname + config = &c + } + + conn := Client(rawConn, config) + + if timeout == 0 { + err = conn.Handshake() + } else { + go func() { + errChannel <- conn.Handshake() + }() + + err = <-errChannel + } + + if err != nil { + rawConn.Close() + return nil, err + } + + return conn, nil +} + +// Dial connects to the given network address using net.Dial +// and then initiates a TLS handshake, returning the resulting +// TLS connection. +// Dial interprets a nil configuration as equivalent to +// the zero configuration; see the documentation of Config +// for the defaults. +func Dial(network, addr string, config *Config) (*Conn, error) { + return DialWithDialer(new(net.Dialer), network, addr, config) +} + +// LoadX509KeyPair reads and parses a public/private key pair from a pair of +// files. The files must contain PEM encoded data. +func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) { + certPEMBlock, err := ioutil.ReadFile(certFile) + if err != nil { + return + } + keyPEMBlock, err := ioutil.ReadFile(keyFile) + if err != nil { + return + } + return X509KeyPair(certPEMBlock, keyPEMBlock) +} + +// X509KeyPair parses a public/private key pair from a pair of +// PEM encoded data. +func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error) { + var certDERBlock *pem.Block + for { + certDERBlock, certPEMBlock = pem.Decode(certPEMBlock) + if certDERBlock == nil { + break + } + if certDERBlock.Type == "CERTIFICATE" { + cert.Certificate = append(cert.Certificate, certDERBlock.Bytes) + } + } + + if len(cert.Certificate) == 0 { + err = errors.New("crypto/tls: failed to parse certificate PEM data") + return + } + + var keyDERBlock *pem.Block + for { + keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock) + if keyDERBlock == nil { + err = errors.New("crypto/tls: failed to parse key PEM data") + return + } + if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") { + break + } + } + + cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes) + if err != nil { + return + } + + // We don't need to parse the public key for TLS, but we so do anyway + // to check that it looks sane and matches the private key. + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) + if err != nil { + return + } + + switch pub := x509Cert.PublicKey.(type) { + case *rsa.PublicKey: + priv, ok := cert.PrivateKey.(*rsa.PrivateKey) + if !ok { + err = errors.New("crypto/tls: private key type does not match public key type") + return + } + if pub.N.Cmp(priv.N) != 0 { + err = errors.New("crypto/tls: private key does not match public key") + return + } + case *ecdsa.PublicKey: + priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey) + if !ok { + err = errors.New("crypto/tls: private key type does not match public key type") + return + + } + if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 { + err = errors.New("crypto/tls: private key does not match public key") + return + } + default: + err = errors.New("crypto/tls: unknown public key algorithm") + return + } + + return +} + +// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates +// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys. +// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three. +func parsePrivateKey(der []byte) (crypto.PrivateKey, error) { + if key, err := x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + if key, err := x509.ParsePKCS8PrivateKey(der); err == nil { + switch key := key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey: + return key, nil + default: + return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping") + } + } + if key, err := x509.ParseECPrivateKey(der); err == nil { + return key, nil + } + + return nil, errors.New("crypto/tls: failed to parse private key") +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/test_config.cc b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/test_config.cc new file mode 100644 index 000000000..e58158197 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/test_config.cc @@ -0,0 +1,264 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include "test_config.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <memory> + +#include <openssl/base64.h> + +namespace { + +template <typename T> +struct Flag { + const char *flag; + T TestConfig::*member; +}; + +// FindField looks for the flag in |flags| that matches |flag|. If one is found, +// it returns a pointer to the corresponding field in |config|. Otherwise, it +// returns NULL. +template<typename T, size_t N> +T *FindField(TestConfig *config, const Flag<T> (&flags)[N], const char *flag) { + for (size_t i = 0; i < N; i++) { + if (strcmp(flag, flags[i].flag) == 0) { + return &(config->*(flags[i].member)); + } + } + return NULL; +} + +const Flag<bool> kBoolFlags[] = { + { "-server", &TestConfig::is_server }, + { "-dtls", &TestConfig::is_dtls }, + { "-fallback-scsv", &TestConfig::fallback_scsv }, + { "-require-any-client-certificate", + &TestConfig::require_any_client_certificate }, + { "-false-start", &TestConfig::false_start }, + { "-async", &TestConfig::async }, + { "-write-different-record-sizes", + &TestConfig::write_different_record_sizes }, + { "-cbc-record-splitting", &TestConfig::cbc_record_splitting }, + { "-partial-write", &TestConfig::partial_write }, + { "-no-tls13", &TestConfig::no_tls13 }, + { "-no-tls12", &TestConfig::no_tls12 }, + { "-no-tls11", &TestConfig::no_tls11 }, + { "-no-tls1", &TestConfig::no_tls1 }, + { "-no-ssl3", &TestConfig::no_ssl3 }, + { "-enable-channel-id", &TestConfig::enable_channel_id }, + { "-shim-writes-first", &TestConfig::shim_writes_first }, + { "-expect-session-miss", &TestConfig::expect_session_miss }, + { "-decline-alpn", &TestConfig::decline_alpn }, + { "-expect-extended-master-secret", + &TestConfig::expect_extended_master_secret }, + { "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling }, + { "-enable-signed-cert-timestamps", + &TestConfig::enable_signed_cert_timestamps }, + { "-implicit-handshake", &TestConfig::implicit_handshake }, + { "-use-early-callback", &TestConfig::use_early_callback }, + { "-fail-early-callback", &TestConfig::fail_early_callback }, + { "-install-ddos-callback", &TestConfig::install_ddos_callback }, + { "-fail-ddos-callback", &TestConfig::fail_ddos_callback }, + { "-fail-second-ddos-callback", &TestConfig::fail_second_ddos_callback }, + { "-fail-cert-callback", &TestConfig::fail_cert_callback }, + { "-handshake-never-done", &TestConfig::handshake_never_done }, + { "-use-export-context", &TestConfig::use_export_context }, + { "-tls-unique", &TestConfig::tls_unique }, + { "-expect-ticket-renewal", &TestConfig::expect_ticket_renewal }, + { "-expect-no-session", &TestConfig::expect_no_session }, + { "-expect-early-data-info", &TestConfig::expect_early_data_info }, + { "-use-ticket-callback", &TestConfig::use_ticket_callback }, + { "-renew-ticket", &TestConfig::renew_ticket }, + { "-enable-early-data", &TestConfig::enable_early_data }, + { "-enable-client-custom-extension", + &TestConfig::enable_client_custom_extension }, + { "-enable-server-custom-extension", + &TestConfig::enable_server_custom_extension }, + { "-custom-extension-skip", &TestConfig::custom_extension_skip }, + { "-custom-extension-fail-add", &TestConfig::custom_extension_fail_add }, + { "-check-close-notify", &TestConfig::check_close_notify }, + { "-shim-shuts-down", &TestConfig::shim_shuts_down }, + { "-verify-fail", &TestConfig::verify_fail }, + { "-verify-peer", &TestConfig::verify_peer }, + { "-expect-verify-result", &TestConfig::expect_verify_result }, + { "-renegotiate-once", &TestConfig::renegotiate_once }, + { "-renegotiate-freely", &TestConfig::renegotiate_freely }, + { "-renegotiate-ignore", &TestConfig::renegotiate_ignore }, + { "-p384-only", &TestConfig::p384_only }, + { "-enable-all-curves", &TestConfig::enable_all_curves }, + { "-use-sparse-dh-prime", &TestConfig::use_sparse_dh_prime }, + { "-use-old-client-cert-callback", + &TestConfig::use_old_client_cert_callback }, + { "-send-alert", &TestConfig::send_alert }, + { "-peek-then-read", &TestConfig::peek_then_read }, + { "-enable-grease", &TestConfig::enable_grease }, + { "-use-exporter-between-reads", &TestConfig::use_exporter_between_reads }, + { "-retain-only-sha256-client-cert-initial", + &TestConfig::retain_only_sha256_client_cert_initial }, + { "-retain-only-sha256-client-cert-resume", + &TestConfig::retain_only_sha256_client_cert_resume }, + { "-expect-sha256-client-cert-initial", + &TestConfig::expect_sha256_client_cert_initial }, + { "-expect-sha256-client-cert-resume", + &TestConfig::expect_sha256_client_cert_resume }, + { "-read-with-unfinished-write", &TestConfig::read_with_unfinished_write }, + { "-expect-secure-renegotiation", + &TestConfig::expect_secure_renegotiation }, + { "-expect-no-secure-renegotiation", + &TestConfig::expect_no_secure_renegotiation }, + { "-expect-session-id", &TestConfig::expect_session_id }, + { "-expect-no-session-id", &TestConfig::expect_no_session_id }, +}; + +const Flag<std::string> kStringFlags[] = { + { "-digest-prefs", &TestConfig::digest_prefs }, + { "-key-file", &TestConfig::key_file }, + { "-cert-file", &TestConfig::cert_file }, + { "-expect-server-name", &TestConfig::expected_server_name }, + { "-advertise-npn", &TestConfig::advertise_npn }, + { "-expect-next-proto", &TestConfig::expected_next_proto }, + { "-select-next-proto", &TestConfig::select_next_proto }, + { "-send-channel-id", &TestConfig::send_channel_id }, + { "-host-name", &TestConfig::host_name }, + { "-advertise-alpn", &TestConfig::advertise_alpn }, + { "-expect-alpn", &TestConfig::expected_alpn }, + { "-expect-advertised-alpn", &TestConfig::expected_advertised_alpn }, + { "-select-alpn", &TestConfig::select_alpn }, + { "-psk", &TestConfig::psk }, + { "-psk-identity", &TestConfig::psk_identity }, + { "-srtp-profiles", &TestConfig::srtp_profiles }, + { "-cipher", &TestConfig::cipher }, + { "-export-label", &TestConfig::export_label }, + { "-export-context", &TestConfig::export_context }, + { "-expect-peer-cert-file", &TestConfig::expect_peer_cert_file }, + { "-use-client-ca-list", &TestConfig::use_client_ca_list }, + { "-expect-client-ca-list", &TestConfig::expected_client_ca_list }, +}; + +const Flag<std::string> kBase64Flags[] = { + { "-expect-certificate-types", &TestConfig::expected_certificate_types }, + { "-expect-channel-id", &TestConfig::expected_channel_id }, + { "-expect-ocsp-response", &TestConfig::expected_ocsp_response }, + { "-expect-signed-cert-timestamps", + &TestConfig::expected_signed_cert_timestamps }, + { "-ocsp-response", &TestConfig::ocsp_response }, + { "-signed-cert-timestamps", &TestConfig::signed_cert_timestamps }, + { "-ticket-key", &TestConfig::ticket_key }, +}; + +const Flag<int> kIntFlags[] = { + { "-port", &TestConfig::port }, + { "-resume-count", &TestConfig::resume_count }, + { "-min-version", &TestConfig::min_version }, + { "-max-version", &TestConfig::max_version }, + { "-mtu", &TestConfig::mtu }, + { "-export-keying-material", &TestConfig::export_keying_material }, + { "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations }, + { "-expect-peer-signature-algorithm", + &TestConfig::expect_peer_signature_algorithm }, + { "-expect-curve-id", &TestConfig::expect_curve_id }, + { "-expect-resume-curve-id", &TestConfig::expect_resume_curve_id }, + { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms }, + { "-max-cert-list", &TestConfig::max_cert_list }, + { "-expect-cipher-aes", &TestConfig::expect_cipher_aes }, + { "-expect-cipher-no-aes", &TestConfig::expect_cipher_no_aes }, + { "-resumption-delay", &TestConfig::resumption_delay }, + { "-max-send-fragment", &TestConfig::max_send_fragment }, + { "-read-size", &TestConfig::read_size }, + { "-expect-ticket-age-skew", &TestConfig::expect_ticket_age_skew }, +}; + +const Flag<std::vector<int>> kIntVectorFlags[] = { + { "-signing-prefs", &TestConfig::signing_prefs }, +}; + +} // namespace + +bool ParseConfig(int argc, char **argv, TestConfig *out_config) { + for (int i = 0; i < argc; i++) { + bool *bool_field = FindField(out_config, kBoolFlags, argv[i]); + if (bool_field != NULL) { + *bool_field = true; + continue; + } + + std::string *string_field = FindField(out_config, kStringFlags, argv[i]); + if (string_field != NULL) { + i++; + if (i >= argc) { + fprintf(stderr, "Missing parameter\n"); + return false; + } + string_field->assign(argv[i]); + continue; + } + + std::string *base64_field = FindField(out_config, kBase64Flags, argv[i]); + if (base64_field != NULL) { + i++; + if (i >= argc) { + fprintf(stderr, "Missing parameter\n"); + return false; + } + size_t len; + if (!EVP_DecodedLength(&len, strlen(argv[i]))) { + fprintf(stderr, "Invalid base64: %s\n", argv[i]); + return false; + } + std::unique_ptr<uint8_t[]> decoded(new uint8_t[len]); + if (!EVP_DecodeBase64(decoded.get(), &len, len, + reinterpret_cast<const uint8_t *>(argv[i]), + strlen(argv[i]))) { + fprintf(stderr, "Invalid base64: %s\n", argv[i]); + return false; + } + base64_field->assign(reinterpret_cast<const char *>(decoded.get()), len); + continue; + } + + int *int_field = FindField(out_config, kIntFlags, argv[i]); + if (int_field) { + i++; + if (i >= argc) { + fprintf(stderr, "Missing parameter\n"); + return false; + } + *int_field = atoi(argv[i]); + continue; + } + + std::vector<int> *int_vector_field = + FindField(out_config, kIntVectorFlags, argv[i]); + if (int_vector_field) { + i++; + if (i >= argc) { + fprintf(stderr, "Missing parameter\n"); + return false; + } + + // Each instance of the flag adds to the list. + int_vector_field->push_back(atoi(argv[i])); + continue; + } + + fprintf(stderr, "Unknown argument: %s\n", argv[i]); + return false; + } + + return true; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/test_config.h b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/test_config.h new file mode 100644 index 000000000..7057b4866 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/test/test_config.h @@ -0,0 +1,142 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef HEADER_TEST_CONFIG +#define HEADER_TEST_CONFIG + +#include <string> +#include <vector> + + +struct TestConfig { + int port = 0; + bool is_server = false; + bool is_dtls = false; + int resume_count = 0; + bool fallback_scsv = false; + std::string digest_prefs; + std::vector<int> signing_prefs; + std::string key_file; + std::string cert_file; + std::string expected_server_name; + std::string expected_certificate_types; + bool require_any_client_certificate = false; + std::string advertise_npn; + std::string expected_next_proto; + bool false_start = false; + std::string select_next_proto; + bool async = false; + bool write_different_record_sizes = false; + bool cbc_record_splitting = false; + bool partial_write = false; + bool no_tls13 = false; + bool no_tls12 = false; + bool no_tls11 = false; + bool no_tls1 = false; + bool no_ssl3 = false; + std::string expected_channel_id; + bool enable_channel_id = false; + std::string send_channel_id; + bool shim_writes_first = false; + std::string host_name; + std::string advertise_alpn; + std::string expected_alpn; + std::string expected_advertised_alpn; + std::string select_alpn; + bool decline_alpn = false; + bool expect_session_miss = false; + bool expect_extended_master_secret = false; + std::string psk; + std::string psk_identity; + std::string srtp_profiles; + bool enable_ocsp_stapling = false; + std::string expected_ocsp_response; + bool enable_signed_cert_timestamps = false; + std::string expected_signed_cert_timestamps; + int min_version = 0; + int max_version = 0; + int mtu = 0; + bool implicit_handshake = false; + bool use_early_callback = false; + bool fail_early_callback = false; + bool install_ddos_callback = false; + bool fail_ddos_callback = false; + bool fail_second_ddos_callback = false; + bool fail_cert_callback = false; + std::string cipher; + bool handshake_never_done = false; + int export_keying_material = 0; + std::string export_label; + std::string export_context; + bool use_export_context = false; + bool tls_unique = false; + bool expect_ticket_renewal = false; + bool expect_no_session = false; + bool expect_early_data_info = false; + bool use_ticket_callback = false; + bool renew_ticket = false; + bool enable_early_data = false; + bool enable_client_custom_extension = false; + bool enable_server_custom_extension = false; + bool custom_extension_skip = false; + bool custom_extension_fail_add = false; + std::string ocsp_response; + bool check_close_notify = false; + bool shim_shuts_down = false; + bool verify_fail = false; + bool verify_peer = false; + bool expect_verify_result = false; + std::string signed_cert_timestamps; + int expect_total_renegotiations = 0; + bool renegotiate_once = false; + bool renegotiate_freely = false; + bool renegotiate_ignore = false; + int expect_peer_signature_algorithm = 0; + bool p384_only = false; + bool enable_all_curves = false; + bool use_sparse_dh_prime = false; + int expect_curve_id = 0; + int expect_resume_curve_id = 0; + bool use_old_client_cert_callback = false; + int initial_timeout_duration_ms = 0; + std::string use_client_ca_list; + std::string expected_client_ca_list; + bool send_alert = false; + bool peek_then_read = false; + bool enable_grease = false; + int max_cert_list = 0; + std::string ticket_key; + bool use_exporter_between_reads = false; + int expect_cipher_aes = 0; + int expect_cipher_no_aes = 0; + std::string expect_peer_cert_file; + int resumption_delay = 0; + bool retain_only_sha256_client_cert_initial = false; + bool retain_only_sha256_client_cert_resume = false; + bool expect_sha256_client_cert_initial = false; + bool expect_sha256_client_cert_resume = false; + bool read_with_unfinished_write = false; + bool expect_secure_renegotiation = false; + bool expect_no_secure_renegotiation = false; + int max_send_fragment = 0; + int read_size = 0; + bool expect_session_id = false; + bool expect_no_session_id = false; + int expect_ticket_age_skew = 0; +}; + +bool ParseConfig(int argc, char **argv, TestConfig *out_config); + + +#endif // HEADER_TEST_CONFIG diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_both.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_both.c new file mode 100644 index 000000000..624392357 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_both.c @@ -0,0 +1,638 @@ +/* Copyright (c) 2016, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> +#include <openssl/hkdf.h> +#include <openssl/mem.h> +#include <openssl/stack.h> +#include <openssl/x509.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +/* kMaxKeyUpdates is the number of consecutive KeyUpdates that will be + * processed. Without this limit an attacker could force unbounded processing + * without being able to return application data. */ +static const uint8_t kMaxKeyUpdates = 32; + +int tls13_handshake(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + for (;;) { + /* Resolve the operation the handshake was waiting on. */ + switch (hs->wait) { + case ssl_hs_error: + OPENSSL_PUT_ERROR(SSL, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + + case ssl_hs_flush: + case ssl_hs_flush_and_read_message: { + int ret = ssl->method->flush_flight(ssl); + if (ret <= 0) { + return ret; + } + if (hs->wait != ssl_hs_flush_and_read_message) { + break; + } + ssl->method->expect_flight(ssl); + hs->wait = ssl_hs_read_message; + /* Fall-through. */ + } + + case ssl_hs_read_message: { + int ret = ssl->method->ssl_get_message(ssl); + if (ret <= 0) { + return ret; + } + break; + } + + case ssl_hs_x509_lookup: + ssl->rwstate = SSL_X509_LOOKUP; + hs->wait = ssl_hs_ok; + return -1; + + case ssl_hs_channel_id_lookup: + ssl->rwstate = SSL_CHANNEL_ID_LOOKUP; + hs->wait = ssl_hs_ok; + return -1; + + case ssl_hs_private_key_operation: + ssl->rwstate = SSL_PRIVATE_KEY_OPERATION; + hs->wait = ssl_hs_ok; + return -1; + + case ssl_hs_pending_ticket: + ssl->rwstate = SSL_PENDING_TICKET; + hs->wait = ssl_hs_ok; + return -1; + + case ssl_hs_ok: + break; + } + + /* Run the state machine again. */ + hs->wait = hs->do_tls13_handshake(hs); + if (hs->wait == ssl_hs_error) { + /* Don't loop around to avoid a stray |SSL_R_SSL_HANDSHAKE_FAILURE| the + * first time around. */ + return -1; + } + if (hs->wait == ssl_hs_ok) { + /* The handshake has completed. */ + return 1; + } + + /* Otherwise, loop to the beginning and resolve what was blocking the + * handshake. */ + } +} + +int tls13_get_cert_verify_signature_input( + SSL_HANDSHAKE *hs, uint8_t **out, size_t *out_len, + enum ssl_cert_verify_context_t cert_verify_context) { + CBB cbb; + if (!CBB_init(&cbb, 64 + 33 + 1 + 2 * EVP_MAX_MD_SIZE)) { + goto err; + } + + for (size_t i = 0; i < 64; i++) { + if (!CBB_add_u8(&cbb, 0x20)) { + goto err; + } + } + + const uint8_t *context; + size_t context_len; + if (cert_verify_context == ssl_cert_verify_server) { + /* Include the NUL byte. */ + static const char kContext[] = "TLS 1.3, server CertificateVerify"; + context = (const uint8_t *)kContext; + context_len = sizeof(kContext); + } else if (cert_verify_context == ssl_cert_verify_client) { + static const char kContext[] = "TLS 1.3, client CertificateVerify"; + context = (const uint8_t *)kContext; + context_len = sizeof(kContext); + } else if (cert_verify_context == ssl_cert_verify_channel_id) { + static const char kContext[] = "TLS 1.3, Channel ID"; + context = (const uint8_t *)kContext; + context_len = sizeof(kContext); + } else { + goto err; + } + + if (!CBB_add_bytes(&cbb, context, context_len)) { + goto err; + } + + uint8_t context_hash[EVP_MAX_MD_SIZE]; + size_t context_hash_len; + if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash, + &context_hash_len) || + !CBB_add_bytes(&cbb, context_hash, context_hash_len) || + !CBB_finish(&cbb, out, out_len)) { + goto err; + } + + return 1; + +err: + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + CBB_cleanup(&cbb); + return 0; +} + +int tls13_process_certificate(SSL_HANDSHAKE *hs, int allow_anonymous) { + SSL *const ssl = hs->ssl; + CBS cbs, context, certificate_list; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + if (!CBS_get_u8_length_prefixed(&cbs, &context) || + CBS_len(&context) != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return 0; + } + + const int retain_sha256 = + ssl->server && ssl->retain_only_sha256_of_client_certs; + int ret = 0; + + EVP_PKEY *pkey = NULL; + STACK_OF(CRYPTO_BUFFER) *certs = sk_CRYPTO_BUFFER_new_null(); + if (certs == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CBS_get_u24_length_prefixed(&cbs, &certificate_list)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto err; + } + + while (CBS_len(&certificate_list) > 0) { + CBS certificate, extensions; + if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate) || + !CBS_get_u16_length_prefixed(&certificate_list, &extensions) || + CBS_len(&certificate) == 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH); + goto err; + } + + if (sk_CRYPTO_BUFFER_num(certs) == 0) { + pkey = ssl_cert_parse_pubkey(&certificate); + if (pkey == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto err; + } + /* TLS 1.3 always uses certificate keys for signing thus the correct + * keyUsage is enforced. */ + if (!ssl_cert_check_digital_signature_key_usage(&certificate)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + goto err; + } + + if (retain_sha256) { + /* Retain the hash of the leaf certificate if requested. */ + SHA256(CBS_data(&certificate), CBS_len(&certificate), + hs->new_session->peer_sha256); + } + } + + CRYPTO_BUFFER *buf = + CRYPTO_BUFFER_new_from_CBS(&certificate, ssl->ctx->pool); + if (buf == NULL || + !sk_CRYPTO_BUFFER_push(certs, buf)) { + CRYPTO_BUFFER_free(buf); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* Parse out the extensions. */ + int have_status_request = 0, have_sct = 0; + CBS status_request, sct; + const SSL_EXTENSION_TYPE ext_types[] = { + {TLSEXT_TYPE_status_request, &have_status_request, &status_request}, + {TLSEXT_TYPE_certificate_timestamp, &have_sct, &sct}, + }; + + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ssl_parse_extensions(&extensions, &alert, ext_types, + OPENSSL_ARRAY_SIZE(ext_types), + 0 /* reject unknown */)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + goto err; + } + + /* All Certificate extensions are parsed, but only the leaf extensions are + * stored. */ + if (have_status_request) { + if (ssl->server || !ssl->ocsp_stapling_enabled) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION); + goto err; + } + + uint8_t status_type; + CBS ocsp_response; + if (!CBS_get_u8(&status_request, &status_type) || + status_type != TLSEXT_STATUSTYPE_ocsp || + !CBS_get_u24_length_prefixed(&status_request, &ocsp_response) || + CBS_len(&ocsp_response) == 0 || + CBS_len(&status_request) != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + goto err; + } + + if (sk_CRYPTO_BUFFER_num(certs) == 1 && + !CBS_stow(&ocsp_response, &hs->new_session->ocsp_response, + &hs->new_session->ocsp_response_length)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + } + + if (have_sct) { + if (ssl->server || !ssl->signed_cert_timestamps_enabled) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION); + goto err; + } + + if (!ssl_is_sct_list_valid(&sct)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + goto err; + } + + if (sk_CRYPTO_BUFFER_num(certs) == 1 && + !CBS_stow( + &sct, &hs->new_session->tlsext_signed_cert_timestamp_list, + &hs->new_session->tlsext_signed_cert_timestamp_list_length)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + } + } + + if (CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + goto err; + } + + EVP_PKEY_free(hs->peer_pubkey); + hs->peer_pubkey = pkey; + pkey = NULL; + + sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free); + hs->new_session->certs = certs; + certs = NULL; + + if (!ssl->ctx->x509_method->session_cache_objects(hs->new_session)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + goto err; + } + + if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) { + if (!allow_anonymous) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED); + goto err; + } + + /* OpenSSL returns X509_V_OK when no certificates are requested. This is + * classed by them as a bug, but it's assumed by at least NGINX. */ + hs->new_session->verify_result = X509_V_OK; + + /* No certificate, so nothing more to do. */ + ret = 1; + goto err; + } + + hs->new_session->peer_sha256_valid = retain_sha256; + + if (!ssl->ctx->x509_method->session_verify_cert_chain(hs->new_session, + ssl)) { + goto err; + } + + ret = 1; + +err: + sk_CRYPTO_BUFFER_pop_free(certs, CRYPTO_BUFFER_free); + EVP_PKEY_free(pkey); + return ret; +} + +int tls13_process_certificate_verify(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + int ret = 0; + uint8_t *msg = NULL; + size_t msg_len; + + if (hs->peer_pubkey == NULL) { + goto err; + } + + CBS cbs, signature; + uint16_t signature_algorithm; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + if (!CBS_get_u16(&cbs, &signature_algorithm) || + !CBS_get_u16_length_prefixed(&cbs, &signature) || + CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + goto err; + } + + int al; + if (!tls12_check_peer_sigalg(ssl, &al, signature_algorithm)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, al); + goto err; + } + hs->new_session->peer_signature_algorithm = signature_algorithm; + + if (!tls13_get_cert_verify_signature_input( + hs, &msg, &msg_len, + ssl->server ? ssl_cert_verify_client : ssl_cert_verify_server)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + + int sig_ok = + ssl_public_key_verify(ssl, CBS_data(&signature), CBS_len(&signature), + signature_algorithm, hs->peer_pubkey, msg, msg_len); +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + sig_ok = 1; + ERR_clear_error(); +#endif + if (!sig_ok) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SIGNATURE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR); + goto err; + } + + ret = 1; + +err: + OPENSSL_free(msg); + return ret; +} + +int tls13_process_finished(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + uint8_t verify_data[EVP_MAX_MD_SIZE]; + size_t verify_data_len; + if (!tls13_finished_mac(hs, verify_data, &verify_data_len, !ssl->server)) { + return 0; + } + + int finished_ok = + ssl->init_num == verify_data_len && + CRYPTO_memcmp(verify_data, ssl->init_msg, verify_data_len) == 0; +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + finished_ok = 1; +#endif + if (!finished_ok) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED); + return 0; + } + + return 1; +} + +int tls13_add_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + CBB cbb, body, certificate_list; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) || + /* The request context is always empty in the handshake. */ + !CBB_add_u8(&body, 0) || + !CBB_add_u24_length_prefixed(&body, &certificate_list)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + + if (!ssl_has_certificate(ssl)) { + if (!ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + return 1; + } + + CERT *cert = ssl->cert; + CRYPTO_BUFFER *leaf_buf = sk_CRYPTO_BUFFER_value(cert->chain, 0); + CBB leaf, extensions; + if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) || + !CBB_add_bytes(&leaf, CRYPTO_BUFFER_data(leaf_buf), + CRYPTO_BUFFER_len(leaf_buf)) || + !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + + if (hs->scts_requested && ssl->cert->signed_cert_timestamp_list != NULL) { + CBB contents; + if (!CBB_add_u16(&extensions, TLSEXT_TYPE_certificate_timestamp) || + !CBB_add_u16_length_prefixed(&extensions, &contents) || + !CBB_add_bytes( + &contents, + CRYPTO_BUFFER_data(ssl->cert->signed_cert_timestamp_list), + CRYPTO_BUFFER_len(ssl->cert->signed_cert_timestamp_list)) || + !CBB_flush(&extensions)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + if (hs->ocsp_stapling_requested && + ssl->cert->ocsp_response != NULL) { + CBB contents, ocsp_response; + if (!CBB_add_u16(&extensions, TLSEXT_TYPE_status_request) || + !CBB_add_u16_length_prefixed(&extensions, &contents) || + !CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) || + !CBB_add_u24_length_prefixed(&contents, &ocsp_response) || + !CBB_add_bytes(&ocsp_response, + CRYPTO_BUFFER_data(ssl->cert->ocsp_response), + CRYPTO_BUFFER_len(ssl->cert->ocsp_response)) || + !CBB_flush(&extensions)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) { + CRYPTO_BUFFER *cert_buf = sk_CRYPTO_BUFFER_value(cert->chain, i); + CBB child; + if (!CBB_add_u24_length_prefixed(&certificate_list, &child) || + !CBB_add_bytes(&child, CRYPTO_BUFFER_data(cert_buf), + CRYPTO_BUFFER_len(cert_buf)) || + !CBB_add_u16(&certificate_list, 0 /* no extensions */)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + if (!ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + return 1; + +err: + CBB_cleanup(&cbb); + return 0; +} + +enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs, + int is_first_run) { + SSL *const ssl = hs->ssl; + enum ssl_private_key_result_t ret = ssl_private_key_failure; + uint8_t *msg = NULL; + size_t msg_len; + CBB cbb, body; + CBB_zero(&cbb); + + uint16_t signature_algorithm; + if (!tls1_choose_signature_algorithm(hs, &signature_algorithm)) { + goto err; + } + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CERTIFICATE_VERIFY) || + !CBB_add_u16(&body, signature_algorithm)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* Sign the digest. */ + CBB child; + const size_t max_sig_len = ssl_private_key_max_signature_len(ssl); + uint8_t *sig; + size_t sig_len; + if (!CBB_add_u16_length_prefixed(&body, &child) || + !CBB_reserve(&child, &sig, max_sig_len)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + + enum ssl_private_key_result_t sign_result; + if (is_first_run) { + if (!tls13_get_cert_verify_signature_input( + hs, &msg, &msg_len, + ssl->server ? ssl_cert_verify_server : ssl_cert_verify_client)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + sign_result = ssl_private_key_sign(ssl, sig, &sig_len, max_sig_len, + signature_algorithm, msg, msg_len); + } else { + sign_result = ssl_private_key_complete(ssl, sig, &sig_len, max_sig_len); + } + + if (sign_result != ssl_private_key_success) { + ret = sign_result; + goto err; + } + + if (!CBB_did_write(&child, sig_len) || + !ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + ret = ssl_private_key_success; + +err: + CBB_cleanup(&cbb); + OPENSSL_free(msg); + return ret; +} + +int tls13_add_finished(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + size_t verify_data_len; + uint8_t verify_data[EVP_MAX_MD_SIZE]; + + if (!tls13_finished_mac(hs, verify_data, &verify_data_len, ssl->server)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED); + return 0; + } + + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) || + !CBB_add_bytes(&body, verify_data, verify_data_len) || + !ssl_add_message_cbb(ssl, &cbb)) { + CBB_cleanup(&cbb); + return 0; + } + + return 1; +} + +static int tls13_receive_key_update(SSL *ssl) { + CBS cbs; + uint8_t key_update_request; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + if (!CBS_get_u8(&cbs, &key_update_request) || + CBS_len(&cbs) != 0 || + (key_update_request != SSL_KEY_UPDATE_NOT_REQUESTED && + key_update_request != SSL_KEY_UPDATE_REQUESTED)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return 0; + } + + /* TODO(svaldez): Send KeyUpdate if |key_update_request| is + * |SSL_KEY_UPDATE_REQUESTED|. */ + return tls13_rotate_traffic_key(ssl, evp_aead_open); +} + +int tls13_post_handshake(SSL *ssl) { + if (ssl->s3->tmp.message_type == SSL3_MT_KEY_UPDATE) { + ssl->s3->key_update_count++; + if (ssl->s3->key_update_count > kMaxKeyUpdates) { + OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_KEY_UPDATES); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return 0; + } + + return tls13_receive_key_update(ssl); + } + + ssl->s3->key_update_count = 0; + + if (ssl->s3->tmp.message_type == SSL3_MT_NEW_SESSION_TICKET && + !ssl->server) { + return tls13_process_new_session_ticket(ssl); + } + + // TODO(svaldez): Handle post-handshake authentication. + + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE); + return 0; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_client.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_client.c new file mode 100644 index 000000000..f13a4f780 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_client.c @@ -0,0 +1,686 @@ +/* Copyright (c) 2016, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <limits.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/digest.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/stack.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +enum client_hs_state_t { + state_process_hello_retry_request = 0, + state_send_second_client_hello, + state_process_server_hello, + state_process_encrypted_extensions, + state_process_certificate_request, + state_process_server_certificate, + state_process_server_certificate_verify, + state_process_server_finished, + state_send_client_certificate, + state_send_client_certificate_verify, + state_complete_client_certificate_verify, + state_complete_second_flight, + state_done, +}; + +static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0}; + +static enum ssl_hs_wait_t do_process_hello_retry_request(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (ssl->s3->tmp.message_type != SSL3_MT_HELLO_RETRY_REQUEST) { + hs->tls13_state = state_process_server_hello; + return ssl_hs_ok; + } + + CBS cbs, extensions; + uint16_t server_wire_version; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + if (!CBS_get_u16(&cbs, &server_wire_version) || + !CBS_get_u16_length_prefixed(&cbs, &extensions) || + /* HelloRetryRequest may not be empty. */ + CBS_len(&extensions) == 0 || + CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } + + int have_cookie, have_key_share; + CBS cookie, key_share; + const SSL_EXTENSION_TYPE ext_types[] = { + {TLSEXT_TYPE_key_share, &have_key_share, &key_share}, + {TLSEXT_TYPE_cookie, &have_cookie, &cookie}, + }; + + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ssl_parse_extensions(&extensions, &alert, ext_types, + OPENSSL_ARRAY_SIZE(ext_types), + 0 /* reject unknown */)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + + if (have_cookie) { + CBS cookie_value; + if (!CBS_get_u16_length_prefixed(&cookie, &cookie_value) || + CBS_len(&cookie_value) == 0 || + CBS_len(&cookie) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } + + if (!CBS_stow(&cookie_value, &hs->cookie, &hs->cookie_len)) { + return ssl_hs_error; + } + } + + if (have_key_share) { + uint16_t group_id; + if (!CBS_get_u16(&key_share, &group_id) || CBS_len(&key_share) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } + + /* The group must be supported. */ + const uint16_t *groups; + size_t groups_len; + tls1_get_grouplist(ssl, &groups, &groups_len); + int found = 0; + for (size_t i = 0; i < groups_len; i++) { + if (groups[i] == group_id) { + found = 1; + break; + } + } + + if (!found) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE); + return ssl_hs_error; + } + + /* Check that the HelloRetryRequest does not request the key share that + * was provided in the initial ClientHello. */ + if (SSL_ECDH_CTX_get_id(&hs->ecdh_ctx) == group_id) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE); + return ssl_hs_error; + } + + SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx); + hs->retry_group = group_id; + } + + if (!ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->received_hello_retry_request = 1; + hs->tls13_state = state_send_second_client_hello; + return ssl_hs_ok; +} + +static enum ssl_hs_wait_t do_send_second_client_hello(SSL_HANDSHAKE *hs) { + if (!ssl_write_client_hello(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_server_hello; + return ssl_hs_flush_and_read_message; +} + +static enum ssl_hs_wait_t do_process_server_hello(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) { + return ssl_hs_error; + } + + CBS cbs, server_random, extensions; + uint16_t server_wire_version; + uint16_t cipher_suite; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + if (!CBS_get_u16(&cbs, &server_wire_version) || + !CBS_get_bytes(&cbs, &server_random, SSL3_RANDOM_SIZE) || + !CBS_get_u16(&cbs, &cipher_suite) || + !CBS_get_u16_length_prefixed(&cbs, &extensions) || + CBS_len(&cbs) != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return ssl_hs_error; + } + + if (server_wire_version != ssl->version) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER); + return ssl_hs_error; + } + + assert(ssl->s3->have_version); + OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random), + SSL3_RANDOM_SIZE); + + const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite); + if (cipher == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return ssl_hs_error; + } + + /* Check if the cipher is a TLS 1.3 cipher. */ + if (SSL_CIPHER_get_min_version(cipher) > ssl3_protocol_version(ssl) || + SSL_CIPHER_get_max_version(cipher) < ssl3_protocol_version(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return ssl_hs_error; + } + + /* Parse out the extensions. */ + int have_key_share = 0, have_pre_shared_key = 0; + CBS key_share, pre_shared_key; + const SSL_EXTENSION_TYPE ext_types[] = { + {TLSEXT_TYPE_key_share, &have_key_share, &key_share}, + {TLSEXT_TYPE_pre_shared_key, &have_pre_shared_key, &pre_shared_key}, + }; + + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ssl_parse_extensions(&extensions, &alert, ext_types, + OPENSSL_ARRAY_SIZE(ext_types), + 0 /* reject unknown */)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + + alert = SSL_AD_DECODE_ERROR; + if (have_pre_shared_key) { + if (ssl->session == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION); + return ssl_hs_error; + } + + if (!ssl_ext_pre_shared_key_parse_serverhello(hs, &alert, + &pre_shared_key)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + + if (ssl->session->ssl_version != ssl->version) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return ssl_hs_error; + } + + if (ssl->session->cipher->algorithm_prf != cipher->algorithm_prf) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_PRF_HASH_MISMATCH); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return ssl_hs_error; + } + + if (!ssl_session_is_context_valid(ssl, ssl->session)) { + /* This is actually a client application bug. */ + OPENSSL_PUT_ERROR(SSL, + SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + return ssl_hs_error; + } + + ssl->s3->session_reused = 1; + /* Only authentication information carries over in TLS 1.3. */ + hs->new_session = SSL_SESSION_dup(ssl->session, SSL_SESSION_DUP_AUTH_ONLY); + if (hs->new_session == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + ssl_set_session(ssl, NULL); + + /* Resumption incorporates fresh key material, so refresh the timeout. */ + ssl_session_renew_timeout(ssl, hs->new_session, + ssl->session_ctx->session_psk_dhe_timeout); + } else if (!ssl_get_new_session(hs, 0)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + + hs->new_session->cipher = cipher; + hs->new_cipher = cipher; + + /* Store the initial negotiated ALPN in the session. */ + if (ssl->s3->alpn_selected != NULL) { + hs->new_session->early_alpn = + BUF_memdup(ssl->s3->alpn_selected, ssl->s3->alpn_selected_len); + if (hs->new_session->early_alpn == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + hs->new_session->early_alpn_len = ssl->s3->alpn_selected_len; + } + + /* The PRF hash is now known. Set up the key schedule. */ + if (!tls13_init_key_schedule(hs)) { + return ssl_hs_error; + } + + /* Incorporate the PSK into the running secret. */ + if (ssl->s3->session_reused) { + if (!tls13_advance_key_schedule(hs, hs->new_session->master_key, + hs->new_session->master_key_length)) { + return ssl_hs_error; + } + } else if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len)) { + return ssl_hs_error; + } + + if (!have_key_share) { + /* We do not support psk_ke and thus always require a key share. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION); + return ssl_hs_error; + } + + /* Resolve ECDHE and incorporate it into the secret. */ + uint8_t *dhe_secret; + size_t dhe_secret_len; + alert = SSL_AD_DECODE_ERROR; + if (!ssl_ext_key_share_parse_serverhello(hs, &dhe_secret, &dhe_secret_len, + &alert, &key_share)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + + if (!tls13_advance_key_schedule(hs, dhe_secret, dhe_secret_len)) { + OPENSSL_free(dhe_secret); + return ssl_hs_error; + } + OPENSSL_free(dhe_secret); + + if (!ssl_hash_current_message(hs) || + !tls13_derive_handshake_secrets(hs) || + !tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret, + hs->hash_len) || + !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret, + hs->hash_len)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_encrypted_extensions; + return ssl_hs_read_message; +} + +static enum ssl_hs_wait_t do_process_encrypted_extensions(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_check_message_type(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS)) { + return ssl_hs_error; + } + + CBS cbs; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + if (!ssl_parse_serverhello_tlsext(hs, &cbs)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT); + return ssl_hs_error; + } + if (CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } + + if (!ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_certificate_request; + return ssl_hs_read_message; +} + +static enum ssl_hs_wait_t do_process_certificate_request(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + /* CertificateRequest may only be sent in non-resumption handshakes. */ + if (ssl->s3->session_reused) { + hs->tls13_state = state_process_server_finished; + return ssl_hs_ok; + } + + /* CertificateRequest is optional. */ + if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) { + hs->tls13_state = state_process_server_certificate; + return ssl_hs_ok; + } + + CBS cbs, context, supported_signature_algorithms; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + if (!CBS_get_u8_length_prefixed(&cbs, &context) || + /* The request context is always empty during the handshake. */ + CBS_len(&context) != 0 || + !CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms) || + CBS_len(&supported_signature_algorithms) == 0 || + !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return ssl_hs_error; + } + + uint8_t alert = SSL_AD_DECODE_ERROR; + STACK_OF(CRYPTO_BUFFER) *ca_names = + ssl_parse_client_CA_list(ssl, &alert, &cbs); + if (ca_names == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + + /* Ignore extensions. */ + CBS extensions; + if (!CBS_get_u16_length_prefixed(&cbs, &extensions) || + CBS_len(&cbs) != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + sk_CRYPTO_BUFFER_pop_free(ca_names, CRYPTO_BUFFER_free); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return ssl_hs_error; + } + + hs->cert_request = 1; + sk_CRYPTO_BUFFER_pop_free(hs->ca_names, CRYPTO_BUFFER_free); + hs->ca_names = ca_names; + ssl->ctx->x509_method->hs_flush_cached_ca_names(hs); + + if (!ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_server_certificate; + return ssl_hs_read_message; +} + +static enum ssl_hs_wait_t do_process_server_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) || + !tls13_process_certificate(hs, 0 /* certificate required */) || + !ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_server_certificate_verify; + return ssl_hs_read_message; +} + +static enum ssl_hs_wait_t do_process_server_certificate_verify( + SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) || + !tls13_process_certificate_verify(hs) || + !ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_server_finished; + return ssl_hs_read_message; +} + +static enum ssl_hs_wait_t do_process_server_finished(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) || + !tls13_process_finished(hs) || + !ssl_hash_current_message(hs) || + /* Update the secret to the master secret and derive traffic keys. */ + !tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) || + !tls13_derive_application_secrets(hs)) { + return ssl_hs_error; + } + + ssl->method->received_flight(ssl); + hs->tls13_state = state_send_client_certificate; + return ssl_hs_ok; +} + +static enum ssl_hs_wait_t do_send_client_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + /* The peer didn't request a certificate. */ + if (!hs->cert_request) { + hs->tls13_state = state_complete_second_flight; + return ssl_hs_ok; + } + + /* Call cert_cb to update the certificate. */ + if (ssl->cert->cert_cb != NULL) { + int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg); + if (rv == 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR); + return ssl_hs_error; + } + if (rv < 0) { + hs->tls13_state = state_send_client_certificate; + return ssl_hs_x509_lookup; + } + } + + if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(ssl) || + !tls13_add_certificate(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_send_client_certificate_verify; + return ssl_hs_ok; +} + +static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs, + int is_first_run) { + SSL *const ssl = hs->ssl; + /* Don't send CertificateVerify if there is no certificate. */ + if (!ssl_has_certificate(ssl)) { + hs->tls13_state = state_complete_second_flight; + return ssl_hs_ok; + } + + switch (tls13_add_certificate_verify(hs, is_first_run)) { + case ssl_private_key_success: + hs->tls13_state = state_complete_second_flight; + return ssl_hs_ok; + + case ssl_private_key_retry: + hs->tls13_state = state_complete_client_certificate_verify; + return ssl_hs_private_key_operation; + + case ssl_private_key_failure: + return ssl_hs_error; + } + + assert(0); + return ssl_hs_error; +} + +static enum ssl_hs_wait_t do_complete_second_flight(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + + /* Send a Channel ID assertion if necessary. */ + if (ssl->s3->tlsext_channel_id_valid) { + if (!ssl_do_channel_id_callback(ssl)) { + hs->tls13_state = state_complete_second_flight; + return ssl_hs_error; + } + + if (ssl->tlsext_channel_id_private == NULL) { + return ssl_hs_channel_id_lookup; + } + + CBB cbb, body; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) || + !tls1_write_channel_id(hs, &body) || + !ssl_add_message_cbb(ssl, &cbb)) { + CBB_cleanup(&cbb); + return ssl_hs_error; + } + } + + /* Send a Finished message. */ + if (!tls13_add_finished(hs)) { + return ssl_hs_error; + } + + /* Derive the final keys and enable them. */ + if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_traffic_secret_0, + hs->hash_len) || + !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_traffic_secret_0, + hs->hash_len) || + !tls13_derive_resumption_secret(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_done; + return ssl_hs_flush; +} + +enum ssl_hs_wait_t tls13_client_handshake(SSL_HANDSHAKE *hs) { + while (hs->tls13_state != state_done) { + enum ssl_hs_wait_t ret = ssl_hs_error; + enum client_hs_state_t state = hs->tls13_state; + switch (state) { + case state_process_hello_retry_request: + ret = do_process_hello_retry_request(hs); + break; + case state_send_second_client_hello: + ret = do_send_second_client_hello(hs); + break; + case state_process_server_hello: + ret = do_process_server_hello(hs); + break; + case state_process_encrypted_extensions: + ret = do_process_encrypted_extensions(hs); + break; + case state_process_certificate_request: + ret = do_process_certificate_request(hs); + break; + case state_process_server_certificate: + ret = do_process_server_certificate(hs); + break; + case state_process_server_certificate_verify: + ret = do_process_server_certificate_verify(hs); + break; + case state_process_server_finished: + ret = do_process_server_finished(hs); + break; + case state_send_client_certificate: + ret = do_send_client_certificate(hs); + break; + case state_send_client_certificate_verify: + ret = do_send_client_certificate_verify(hs, 1 /* first run */); + break; + case state_complete_client_certificate_verify: + ret = do_send_client_certificate_verify(hs, 0 /* complete */); + break; + case state_complete_second_flight: + ret = do_complete_second_flight(hs); + break; + case state_done: + ret = ssl_hs_ok; + break; + } + + if (ret != ssl_hs_ok) { + return ret; + } + } + + return ssl_hs_ok; +} + +int tls13_process_new_session_ticket(SSL *ssl) { + int ret = 0; + SSL_SESSION *session = SSL_SESSION_dup(ssl->s3->established_session, + SSL_SESSION_INCLUDE_NONAUTH); + if (session == NULL) { + return 0; + } + + ssl_session_rebase_time(ssl, session); + + uint32_t server_timeout; + CBS cbs, ticket, extensions; + CBS_init(&cbs, ssl->init_msg, ssl->init_num); + if (!CBS_get_u32(&cbs, &server_timeout) || + !CBS_get_u32(&cbs, &session->ticket_age_add) || + !CBS_get_u16_length_prefixed(&cbs, &ticket) || + !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) || + !CBS_get_u16_length_prefixed(&cbs, &extensions) || + CBS_len(&cbs) != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto err; + } + + /* Cap the renewable lifetime by the server advertised value. This avoids + * wasting bandwidth on 0-RTT when we know the server will reject it. */ + if (session->timeout > server_timeout) { + session->timeout = server_timeout; + } + + /* Parse out the extensions. */ + int have_early_data_info = 0; + CBS early_data_info; + const SSL_EXTENSION_TYPE ext_types[] = { + {TLSEXT_TYPE_ticket_early_data_info, &have_early_data_info, + &early_data_info}, + }; + + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ssl_parse_extensions(&extensions, &alert, ext_types, + OPENSSL_ARRAY_SIZE(ext_types), + 1 /* ignore unknown */)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + goto err; + } + + if (have_early_data_info && ssl->ctx->enable_early_data) { + if (!CBS_get_u32(&early_data_info, &session->ticket_max_early_data) || + CBS_len(&early_data_info) != 0) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + goto err; + } + } + + session->ticket_age_add_valid = 1; + session->not_resumable = 0; + + if (ssl->ctx->new_session_cb != NULL && + ssl->ctx->new_session_cb(ssl, session)) { + /* |new_session_cb|'s return value signals that it took ownership. */ + session = NULL; + } + + ret = 1; + +err: + SSL_SESSION_free(session); + return ret; +} + +void ssl_clear_tls13_state(SSL_HANDSHAKE *hs) { + SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx); + + OPENSSL_free(hs->key_share_bytes); + hs->key_share_bytes = NULL; + hs->key_share_bytes_len = 0; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_enc.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_enc.c new file mode 100644 index 000000000..412705da8 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_enc.c @@ -0,0 +1,430 @@ +/* Copyright (c) 2016, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/aead.h> +#include <openssl/bytestring.h> +#include <openssl/digest.h> +#include <openssl/hkdf.h> +#include <openssl/hmac.h> +#include <openssl/mem.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +int tls13_init_key_schedule(SSL_HANDSHAKE *hs) { + if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(hs->ssl), + hs->new_cipher->algorithm_prf)) { + return 0; + } + + + hs->hash_len = SSL_TRANSCRIPT_digest_len(&hs->transcript); + + /* Initialize the secret to the zero key. */ + OPENSSL_memset(hs->secret, 0, hs->hash_len); + + SSL_TRANSCRIPT_free_buffer(&hs->transcript); + return 1; +} + +int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in, + size_t len) { + return HKDF_extract(hs->secret, &hs->hash_len, + SSL_TRANSCRIPT_md(&hs->transcript), in, len, hs->secret, + hs->hash_len); +} + +static int hkdf_expand_label(uint8_t *out, const EVP_MD *digest, + const uint8_t *secret, size_t secret_len, + const uint8_t *label, size_t label_len, + const uint8_t *hash, size_t hash_len, size_t len) { + static const char kTLS13LabelVersion[] = "TLS 1.3, "; + + CBB cbb, child; + uint8_t *hkdf_label; + size_t hkdf_label_len; + if (!CBB_init(&cbb, 2 + 1 + strlen(kTLS13LabelVersion) + label_len + 1 + + hash_len) || + !CBB_add_u16(&cbb, len) || + !CBB_add_u8_length_prefixed(&cbb, &child) || + !CBB_add_bytes(&child, (const uint8_t *)kTLS13LabelVersion, + strlen(kTLS13LabelVersion)) || + !CBB_add_bytes(&child, label, label_len) || + !CBB_add_u8_length_prefixed(&cbb, &child) || + !CBB_add_bytes(&child, hash, hash_len) || + !CBB_finish(&cbb, &hkdf_label, &hkdf_label_len)) { + CBB_cleanup(&cbb); + return 0; + } + + int ret = HKDF_expand(out, len, digest, secret, secret_len, hkdf_label, + hkdf_label_len); + OPENSSL_free(hkdf_label); + return ret; +} + +/* derive_secret derives a secret of length |len| and writes the result in |out| + * with the given label and the current base secret and most recently-saved + * handshake context. It returns one on success and zero on error. */ +static int derive_secret(SSL_HANDSHAKE *hs, uint8_t *out, size_t len, + const uint8_t *label, size_t label_len) { + uint8_t context_hash[EVP_MAX_MD_SIZE]; + size_t context_hash_len; + if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash, + &context_hash_len)) { + return 0; + } + + return hkdf_expand_label(out, SSL_TRANSCRIPT_md(&hs->transcript), hs->secret, + hs->hash_len, label, label_len, context_hash, + context_hash_len, len); +} + +int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction, + const uint8_t *traffic_secret, + size_t traffic_secret_len) { + if (traffic_secret_len > 0xff) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + + /* Look up cipher suite properties. */ + const EVP_AEAD *aead; + size_t discard; + if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, + SSL_get_session(ssl)->cipher, + ssl3_protocol_version(ssl))) { + return 0; + } + + const EVP_MD *digest = ssl_get_handshake_digest( + SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl)); + + /* Derive the key. */ + size_t key_len = EVP_AEAD_key_length(aead); + uint8_t key[EVP_AEAD_MAX_KEY_LENGTH]; + if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len, + (const uint8_t *)"key", 3, NULL, 0, key_len)) { + return 0; + } + + /* Derive the IV. */ + size_t iv_len = EVP_AEAD_nonce_length(aead); + uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH]; + if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len, + (const uint8_t *)"iv", 2, NULL, 0, iv_len)) { + return 0; + } + + SSL_AEAD_CTX *traffic_aead = SSL_AEAD_CTX_new( + direction, ssl3_protocol_version(ssl), SSL_get_session(ssl)->cipher, key, + key_len, NULL, 0, iv, iv_len); + if (traffic_aead == NULL) { + return 0; + } + + if (direction == evp_aead_open) { + if (!ssl->method->set_read_state(ssl, traffic_aead)) { + return 0; + } + } else { + if (!ssl->method->set_write_state(ssl, traffic_aead)) { + return 0; + } + } + + /* Save the traffic secret. */ + if (direction == evp_aead_open) { + OPENSSL_memmove(ssl->s3->read_traffic_secret, traffic_secret, + traffic_secret_len); + ssl->s3->read_traffic_secret_len = traffic_secret_len; + } else { + OPENSSL_memmove(ssl->s3->write_traffic_secret, traffic_secret, + traffic_secret_len); + ssl->s3->write_traffic_secret_len = traffic_secret_len; + } + + return 1; +} + +static const char kTLS13LabelClientHandshakeTraffic[] = + "client handshake traffic secret"; +static const char kTLS13LabelServerHandshakeTraffic[] = + "server handshake traffic secret"; +static const char kTLS13LabelClientApplicationTraffic[] = + "client application traffic secret"; +static const char kTLS13LabelServerApplicationTraffic[] = + "server application traffic secret"; + +int tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + return derive_secret(hs, hs->client_handshake_secret, hs->hash_len, + (const uint8_t *)kTLS13LabelClientHandshakeTraffic, + strlen(kTLS13LabelClientHandshakeTraffic)) && + ssl_log_secret(ssl, "CLIENT_HANDSHAKE_TRAFFIC_SECRET", + hs->client_handshake_secret, hs->hash_len) && + derive_secret(hs, hs->server_handshake_secret, hs->hash_len, + (const uint8_t *)kTLS13LabelServerHandshakeTraffic, + strlen(kTLS13LabelServerHandshakeTraffic)) && + ssl_log_secret(ssl, "SERVER_HANDSHAKE_TRAFFIC_SECRET", + hs->server_handshake_secret, hs->hash_len); +} + +static const char kTLS13LabelExporter[] = "exporter master secret"; + +int tls13_derive_application_secrets(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + ssl->s3->exporter_secret_len = hs->hash_len; + return derive_secret(hs, hs->client_traffic_secret_0, hs->hash_len, + (const uint8_t *)kTLS13LabelClientApplicationTraffic, + strlen(kTLS13LabelClientApplicationTraffic)) && + ssl_log_secret(ssl, "CLIENT_TRAFFIC_SECRET_0", + hs->client_traffic_secret_0, hs->hash_len) && + derive_secret(hs, hs->server_traffic_secret_0, hs->hash_len, + (const uint8_t *)kTLS13LabelServerApplicationTraffic, + strlen(kTLS13LabelServerApplicationTraffic)) && + ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0", + hs->server_traffic_secret_0, hs->hash_len) && + derive_secret(hs, ssl->s3->exporter_secret, hs->hash_len, + (const uint8_t *)kTLS13LabelExporter, + strlen(kTLS13LabelExporter)); +} + +static const char kTLS13LabelApplicationTraffic[] = + "application traffic secret"; + +int tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) { + const EVP_MD *digest = ssl_get_handshake_digest( + SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl)); + + uint8_t *secret; + size_t secret_len; + if (direction == evp_aead_open) { + secret = ssl->s3->read_traffic_secret; + secret_len = ssl->s3->read_traffic_secret_len; + } else { + secret = ssl->s3->write_traffic_secret; + secret_len = ssl->s3->write_traffic_secret_len; + } + + if (!hkdf_expand_label(secret, digest, secret, secret_len, + (const uint8_t *)kTLS13LabelApplicationTraffic, + strlen(kTLS13LabelApplicationTraffic), NULL, 0, + secret_len)) { + return 0; + } + + return tls13_set_traffic_key(ssl, direction, secret, secret_len); +} + +static const char kTLS13LabelResumption[] = "resumption master secret"; + +int tls13_derive_resumption_secret(SSL_HANDSHAKE *hs) { + if (hs->hash_len > SSL_MAX_MASTER_KEY_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + hs->new_session->master_key_length = hs->hash_len; + return derive_secret( + hs, hs->new_session->master_key, hs->new_session->master_key_length, + (const uint8_t *)kTLS13LabelResumption, strlen(kTLS13LabelResumption)); +} + +static const char kTLS13LabelFinished[] = "finished"; + +/* tls13_verify_data sets |out| to be the HMAC of |context| using a derived + * Finished key for both Finished messages and the PSK binder. */ +static int tls13_verify_data(const EVP_MD *digest, uint8_t *out, + size_t *out_len, const uint8_t *secret, + size_t hash_len, uint8_t *context, + size_t context_len) { + uint8_t key[EVP_MAX_MD_SIZE]; + unsigned len; + if (!hkdf_expand_label(key, digest, secret, hash_len, + (const uint8_t *)kTLS13LabelFinished, + strlen(kTLS13LabelFinished), NULL, 0, hash_len) || + HMAC(digest, key, hash_len, context, context_len, out, &len) == NULL) { + return 0; + } + *out_len = len; + return 1; +} + +int tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, + int is_server) { + SSL *const ssl = hs->ssl; + + const uint8_t *traffic_secret; + if (is_server == ssl->server) { + traffic_secret = ssl->s3->write_traffic_secret; + } else { + traffic_secret = ssl->s3->read_traffic_secret; + } + + uint8_t context_hash[EVP_MAX_MD_SIZE]; + size_t context_hash_len; + if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash, + &context_hash_len) || + !tls13_verify_data(SSL_TRANSCRIPT_md(&hs->transcript), out, out_len, + traffic_secret, hs->hash_len, context_hash, + context_hash_len)) { + return 0; + } + return 1; +} + +int tls13_export_keying_material(SSL *ssl, uint8_t *out, size_t out_len, + const char *label, size_t label_len, + const uint8_t *context, size_t context_len, + int use_context) { + const EVP_MD *digest = ssl_get_handshake_digest( + SSL_get_session(ssl)->cipher->algorithm_prf, ssl3_protocol_version(ssl)); + + const uint8_t *hash = NULL; + size_t hash_len = 0; + if (use_context) { + hash = context; + hash_len = context_len; + } + return hkdf_expand_label(out, digest, ssl->s3->exporter_secret, + ssl->s3->exporter_secret_len, (const uint8_t *)label, + label_len, hash, hash_len, out_len); +} + +static const char kTLS13LabelPSKBinder[] = "resumption psk binder key"; + +static int tls13_psk_binder(uint8_t *out, const EVP_MD *digest, uint8_t *psk, + size_t psk_len, uint8_t *context, + size_t context_len, size_t hash_len) { + uint8_t binder_context[EVP_MAX_MD_SIZE]; + unsigned binder_context_len; + if (!EVP_Digest(NULL, 0, binder_context, &binder_context_len, digest, NULL)) { + return 0; + } + + uint8_t early_secret[EVP_MAX_MD_SIZE] = {0}; + size_t early_secret_len; + if (!HKDF_extract(early_secret, &early_secret_len, digest, psk, hash_len, + NULL, 0)) { + return 0; + } + + uint8_t binder_key[EVP_MAX_MD_SIZE] = {0}; + size_t len; + if (!hkdf_expand_label(binder_key, digest, early_secret, hash_len, + (const uint8_t *)kTLS13LabelPSKBinder, + strlen(kTLS13LabelPSKBinder), binder_context, + binder_context_len, hash_len) || + !tls13_verify_data(digest, out, &len, binder_key, hash_len, context, + context_len)) { + return 0; + } + + return 1; +} + +int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) { + SSL *const ssl = hs->ssl; + const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl); + if (digest == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + size_t hash_len = EVP_MD_size(digest); + + if (len < hash_len + 3) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + EVP_MD_CTX ctx; + EVP_MD_CTX_init(&ctx); + uint8_t context[EVP_MAX_MD_SIZE]; + unsigned context_len; + if (!EVP_DigestInit_ex(&ctx, digest, NULL) || + !EVP_DigestUpdate(&ctx, hs->transcript.buffer->data, + hs->transcript.buffer->length) || + !EVP_DigestUpdate(&ctx, msg, len - hash_len - 3) || + !EVP_DigestFinal_ex(&ctx, context, &context_len)) { + EVP_MD_CTX_cleanup(&ctx); + return 0; + } + + EVP_MD_CTX_cleanup(&ctx); + + uint8_t verify_data[EVP_MAX_MD_SIZE] = {0}; + if (!tls13_psk_binder(verify_data, digest, ssl->session->master_key, + ssl->session->master_key_length, context, context_len, + hash_len)) { + return 0; + } + + OPENSSL_memcpy(msg + len - hash_len, verify_data, hash_len); + return 1; +} + +int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session, + CBS *binders) { + size_t hash_len = SSL_TRANSCRIPT_digest_len(&hs->transcript); + + /* Get the full ClientHello, including message header. It must be large enough + * to exclude the binders. */ + CBS message; + hs->ssl->method->get_current_message(hs->ssl, &message); + if (CBS_len(&message) < CBS_len(binders) + 2) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* Hash a ClientHello prefix up to the binders. For now, this assumes we only + * ever verify PSK binders on initial ClientHellos. */ + uint8_t context[EVP_MAX_MD_SIZE]; + unsigned context_len; + if (!EVP_Digest(CBS_data(&message), CBS_len(&message) - CBS_len(binders) - 2, + context, &context_len, SSL_TRANSCRIPT_md(&hs->transcript), + NULL)) { + return 0; + } + + uint8_t verify_data[EVP_MAX_MD_SIZE] = {0}; + CBS binder; + if (!tls13_psk_binder(verify_data, SSL_TRANSCRIPT_md(&hs->transcript), + session->master_key, session->master_key_length, + context, context_len, hash_len) || + /* We only consider the first PSK, so compare against the first binder. */ + !CBS_get_u8_length_prefixed(binders, &binder)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return 0; + } + + int binder_ok = + CBS_len(&binder) == hash_len && + CRYPTO_memcmp(CBS_data(&binder), verify_data, hash_len) == 0; +#if defined(BORINGSSL_UNSAFE_FUZZER_MODE) + binder_ok = 1; +#endif + if (!binder_ok) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED); + return 0; + } + + return 1; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_server.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_server.c new file mode 100644 index 000000000..9c8d1a15d --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls13_server.c @@ -0,0 +1,753 @@ +/* Copyright (c) 2016, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/aead.h> +#include <openssl/bytestring.h> +#include <openssl/digest.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/rand.h> +#include <openssl/stack.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +/* kMaxEarlyDataAccepted is the advertised number of plaintext bytes of early + * data that will be accepted. This value should be slightly below + * kMaxEarlyDataSkipped in tls_record.c, which is measured in ciphertext. */ +static const size_t kMaxEarlyDataAccepted = 14336; + +enum server_hs_state_t { + state_select_parameters = 0, + state_select_session, + state_send_hello_retry_request, + state_process_second_client_hello, + state_send_server_hello, + state_send_server_certificate_verify, + state_complete_server_certificate_verify, + state_send_server_finished, + state_process_client_certificate, + state_process_client_certificate_verify, + state_process_channel_id, + state_process_client_finished, + state_send_new_session_ticket, + state_done, +}; + +static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0}; + +static int resolve_ecdhe_secret(SSL_HANDSHAKE *hs, int *out_need_retry, + SSL_CLIENT_HELLO *client_hello) { + SSL *const ssl = hs->ssl; + *out_need_retry = 0; + + /* We only support connections that include an ECDHE key exchange. */ + CBS key_share; + if (!ssl_client_hello_get_extension(client_hello, &key_share, + TLSEXT_TYPE_key_share)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION); + return 0; + } + + int found_key_share; + uint8_t *dhe_secret; + size_t dhe_secret_len; + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ssl_ext_key_share_parse_clienthello(hs, &found_key_share, &dhe_secret, + &dhe_secret_len, &alert, + &key_share)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return 0; + } + + if (!found_key_share) { + *out_need_retry = 1; + return 0; + } + + int ok = tls13_advance_key_schedule(hs, dhe_secret, dhe_secret_len); + OPENSSL_free(dhe_secret); + return ok; +} + +static const SSL_CIPHER *choose_tls13_cipher( + const SSL *ssl, const SSL_CLIENT_HELLO *client_hello) { + if (client_hello->cipher_suites_len % 2 != 0) { + return NULL; + } + + CBS cipher_suites; + CBS_init(&cipher_suites, client_hello->cipher_suites, + client_hello->cipher_suites_len); + + const int aes_is_fine = EVP_has_aes_hardware(); + const uint16_t version = ssl3_protocol_version(ssl); + + const SSL_CIPHER *best = NULL; + while (CBS_len(&cipher_suites) > 0) { + uint16_t cipher_suite; + if (!CBS_get_u16(&cipher_suites, &cipher_suite)) { + return NULL; + } + + /* Limit to TLS 1.3 ciphers we know about. */ + const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite); + if (candidate == NULL || + SSL_CIPHER_get_min_version(candidate) > version || + SSL_CIPHER_get_max_version(candidate) < version) { + continue; + } + + /* TLS 1.3 removes legacy ciphers, so honor the client order, but prefer + * ChaCha20 if we do not have AES hardware. */ + if (aes_is_fine) { + return candidate; + } + + if (candidate->algorithm_enc == SSL_CHACHA20POLY1305) { + return candidate; + } + + if (best == NULL) { + best = candidate; + } + } + + return best; +} + +static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { + /* At this point, most ClientHello extensions have already been processed by + * the common handshake logic. Resolve the remaining non-PSK parameters. */ + SSL *const ssl = hs->ssl; + + SSL_CLIENT_HELLO client_hello; + if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg, + ssl->init_num)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } + + /* Negotiate the cipher suite. */ + hs->new_cipher = choose_tls13_cipher(ssl, &client_hello); + if (hs->new_cipher == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_CIPHER); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + return ssl_hs_error; + } + + /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was + * deferred. Complete it now. */ + uint8_t alert = SSL_AD_DECODE_ERROR; + if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + } + + /* The PRF hash is now known. Set up the key schedule and hash the + * ClientHello. */ + if (!tls13_init_key_schedule(hs) || + !ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_select_session; + return ssl_hs_ok; +} + +static enum ssl_ticket_aead_result_t select_session( + SSL_HANDSHAKE *hs, uint8_t *out_alert, SSL_SESSION **out_session, + int32_t *out_ticket_age_skew, const SSL_CLIENT_HELLO *client_hello) { + SSL *const ssl = hs->ssl; + *out_session = NULL; + + /* Decode the ticket if we agreed on a PSK key exchange mode. */ + CBS pre_shared_key; + if (!hs->accept_psk_mode || + !ssl_client_hello_get_extension(client_hello, &pre_shared_key, + TLSEXT_TYPE_pre_shared_key)) { + return ssl_ticket_aead_ignore_ticket; + } + + /* Verify that the pre_shared_key extension is the last extension in + * ClientHello. */ + if (CBS_data(&pre_shared_key) + CBS_len(&pre_shared_key) != + client_hello->extensions + client_hello->extensions_len) { + OPENSSL_PUT_ERROR(SSL, SSL_R_PRE_SHARED_KEY_MUST_BE_LAST); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return ssl_ticket_aead_error; + } + + CBS ticket, binders; + uint32_t client_ticket_age; + if (!ssl_ext_pre_shared_key_parse_clienthello(hs, &ticket, &binders, + &client_ticket_age, out_alert, + &pre_shared_key)) { + return ssl_ticket_aead_error; + } + + /* TLS 1.3 session tickets are renewed separately as part of the + * NewSessionTicket. */ + int unused_renew; + SSL_SESSION *session = NULL; + enum ssl_ticket_aead_result_t ret = + ssl_process_ticket(ssl, &session, &unused_renew, CBS_data(&ticket), + CBS_len(&ticket), NULL, 0); + switch (ret) { + case ssl_ticket_aead_success: + break; + case ssl_ticket_aead_error: + *out_alert = SSL_AD_INTERNAL_ERROR; + return ret; + default: + return ret; + } + + if (!ssl_session_is_resumable(hs, session) || + /* Historically, some TLS 1.3 tickets were missing ticket_age_add. */ + !session->ticket_age_add_valid) { + SSL_SESSION_free(session); + return ssl_ticket_aead_ignore_ticket; + } + + /* Recover the client ticket age and convert to seconds. */ + client_ticket_age -= session->ticket_age_add; + client_ticket_age /= 1000; + + struct OPENSSL_timeval now; + ssl_get_current_time(ssl, &now); + + /* Compute the server ticket age in seconds. */ + assert(now.tv_sec >= session->time); + uint64_t server_ticket_age = now.tv_sec - session->time; + + /* To avoid overflowing |hs->ticket_age_skew|, we will not resume + * 68-year-old sessions. */ + if (server_ticket_age > INT32_MAX) { + SSL_SESSION_free(session); + return ssl_ticket_aead_ignore_ticket; + } + + /* TODO(davidben,svaldez): Measure this value to decide on tolerance. For + * now, accept all values. https://crbug.com/boringssl/113. */ + *out_ticket_age_skew = + (int32_t)client_ticket_age - (int32_t)server_ticket_age; + + /* Check the PSK binder. */ + if (!tls13_verify_psk_binder(hs, session, &binders)) { + SSL_SESSION_free(session); + *out_alert = SSL_AD_DECRYPT_ERROR; + return ssl_ticket_aead_error; + } + + *out_session = session; + return ssl_ticket_aead_success; +} + +static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + SSL_CLIENT_HELLO client_hello; + if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg, + ssl->init_num)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } + + uint8_t alert = SSL_AD_DECODE_ERROR; + SSL_SESSION *session = NULL; + switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew, + &client_hello)) { + case ssl_ticket_aead_ignore_ticket: + assert(session == NULL); + if (!ssl_get_new_session(hs, 1 /* server */)) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + break; + + case ssl_ticket_aead_success: + /* Carry over authentication information from the previous handshake into + * a fresh session. */ + hs->new_session = SSL_SESSION_dup(session, SSL_SESSION_DUP_AUTH_ONLY); + SSL_SESSION_free(session); + if (hs->new_session == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + + ssl->s3->session_reused = 1; + + /* Resumption incorporates fresh key material, so refresh the timeout. */ + ssl_session_renew_timeout(ssl, hs->new_session, + ssl->session_ctx->session_psk_dhe_timeout); + break; + + case ssl_ticket_aead_error: + ssl3_send_alert(ssl, SSL3_AL_FATAL, alert); + return ssl_hs_error; + + case ssl_ticket_aead_retry: + hs->tls13_state = state_select_session; + return ssl_hs_pending_ticket; + } + + /* Record connection properties in the new session. */ + hs->new_session->cipher = hs->new_cipher; + + if (hs->hostname != NULL) { + OPENSSL_free(hs->new_session->tlsext_hostname); + hs->new_session->tlsext_hostname = BUF_strdup(hs->hostname); + if (hs->new_session->tlsext_hostname == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + } + + if (ssl->s3->alpn_selected != NULL) { + hs->new_session->early_alpn = + BUF_memdup(ssl->s3->alpn_selected, ssl->s3->alpn_selected_len); + if (hs->new_session->early_alpn == NULL) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + hs->new_session->early_alpn_len = ssl->s3->alpn_selected_len; + } + + if (ssl->ctx->dos_protection_cb != NULL && + ssl->ctx->dos_protection_cb(&client_hello) == 0) { + /* Connection rejected for DOS reasons. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return ssl_hs_error; + } + + /* Incorporate the PSK into the running secret. */ + if (ssl->s3->session_reused) { + if (!tls13_advance_key_schedule(hs, hs->new_session->master_key, + hs->new_session->master_key_length)) { + return ssl_hs_error; + } + } else if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len)) { + return ssl_hs_error; + } + + ssl->method->received_flight(ssl); + + /* Resolve ECDHE and incorporate it into the secret. */ + int need_retry; + if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) { + if (need_retry) { + hs->tls13_state = state_send_hello_retry_request; + return ssl_hs_ok; + } + return ssl_hs_error; + } + + hs->tls13_state = state_send_server_hello; + return ssl_hs_ok; +} + +static enum ssl_hs_wait_t do_send_hello_retry_request(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + CBB cbb, body, extensions; + uint16_t group_id; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_HELLO_RETRY_REQUEST) || + !CBB_add_u16(&body, ssl->version) || + !tls1_get_shared_group(hs, &group_id) || + !CBB_add_u16_length_prefixed(&body, &extensions) || + !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) || + !CBB_add_u16(&extensions, 2 /* length */) || + !CBB_add_u16(&extensions, group_id) || + !ssl_add_message_cbb(ssl, &cbb)) { + CBB_cleanup(&cbb); + return ssl_hs_error; + } + + hs->tls13_state = state_process_second_client_hello; + return ssl_hs_flush_and_read_message; +} + +static enum ssl_hs_wait_t do_process_second_client_hello(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) { + return ssl_hs_error; + } + + SSL_CLIENT_HELLO client_hello; + if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg, + ssl->init_num)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + return ssl_hs_error; + } + + int need_retry; + if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) { + if (need_retry) { + /* Only send one HelloRetryRequest. */ + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE); + } + return ssl_hs_error; + } + + if (!ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + ssl->method->received_flight(ssl); + hs->tls13_state = state_send_server_hello; + return ssl_hs_ok; +} + +static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + + /* Send a ServerHello. */ + CBB cbb, body, extensions; + if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) || + !CBB_add_u16(&body, ssl->version) || + !RAND_bytes(ssl->s3->server_random, sizeof(ssl->s3->server_random)) || + !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) || + !CBB_add_u16(&body, ssl_cipher_get_value(hs->new_cipher)) || + !CBB_add_u16_length_prefixed(&body, &extensions) || + !ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) || + !ssl_ext_key_share_add_serverhello(hs, &extensions) || + !ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + /* Derive and enable the handshake traffic secrets. */ + if (!tls13_derive_handshake_secrets(hs) || + !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret, + hs->hash_len) || + !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_handshake_secret, + hs->hash_len)) { + goto err; + } + + /* Send EncryptedExtensions. */ + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_ENCRYPTED_EXTENSIONS) || + !ssl_add_serverhello_tlsext(hs, &body) || + !ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + + /* Determine whether to request a client certificate. */ + hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER); + /* CertificateRequest may only be sent in non-resumption handshakes. */ + if (ssl->s3->session_reused) { + hs->cert_request = 0; + } + + /* Send a CertificateRequest, if necessary. */ + if (hs->cert_request) { + CBB sigalgs_cbb; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_CERTIFICATE_REQUEST) || + !CBB_add_u8(&body, 0 /* no certificate_request_context. */)) { + goto err; + } + + const uint16_t *sigalgs; + size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs); + if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) { + goto err; + } + + for (size_t i = 0; i < num_sigalgs; i++) { + if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) { + goto err; + } + } + + if (!ssl_add_client_CA_list(ssl, &body) || + !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) || + !ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + } + + /* Send the server Certificate message, if necessary. */ + if (!ssl->s3->session_reused) { + if (!ssl_has_certificate(ssl)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET); + goto err; + } + + if (!tls13_add_certificate(hs)) { + goto err; + } + + hs->tls13_state = state_send_server_certificate_verify; + return ssl_hs_ok; + } + + hs->tls13_state = state_send_server_finished; + return ssl_hs_ok; + +err: + CBB_cleanup(&cbb); + return ssl_hs_error; +} + +static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL_HANDSHAKE *hs, + int is_first_run) { + switch (tls13_add_certificate_verify(hs, is_first_run)) { + case ssl_private_key_success: + hs->tls13_state = state_send_server_finished; + return ssl_hs_ok; + + case ssl_private_key_retry: + hs->tls13_state = state_complete_server_certificate_verify; + return ssl_hs_private_key_operation; + + case ssl_private_key_failure: + return ssl_hs_error; + } + + assert(0); + return ssl_hs_error; +} + +static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!tls13_add_finished(hs) || + /* Update the secret to the master secret and derive traffic keys. */ + !tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) || + !tls13_derive_application_secrets(hs) || + !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_traffic_secret_0, + hs->hash_len)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_client_certificate; + return ssl_hs_flush_and_read_message; +} + +static enum ssl_hs_wait_t do_process_client_certificate(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!hs->cert_request) { + /* OpenSSL returns X509_V_OK when no certificates are requested. This is + * classed by them as a bug, but it's assumed by at least NGINX. */ + hs->new_session->verify_result = X509_V_OK; + + /* Skip this state. */ + hs->tls13_state = state_process_channel_id; + return ssl_hs_ok; + } + + const int allow_anonymous = + (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0; + + if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) || + !tls13_process_certificate(hs, allow_anonymous) || + !ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_client_certificate_verify; + return ssl_hs_read_message; +} + +static enum ssl_hs_wait_t do_process_client_certificate_verify( + SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) { + /* Skip this state. */ + hs->tls13_state = state_process_channel_id; + return ssl_hs_ok; + } + + if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) || + !tls13_process_certificate_verify(hs) || + !ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_channel_id; + return ssl_hs_read_message; +} + +static enum ssl_hs_wait_t do_process_channel_id(SSL_HANDSHAKE *hs) { + if (!hs->ssl->s3->tlsext_channel_id_valid) { + hs->tls13_state = state_process_client_finished; + return ssl_hs_ok; + } + + if (!ssl_check_message_type(hs->ssl, SSL3_MT_CHANNEL_ID) || + !tls1_verify_channel_id(hs) || + !ssl_hash_current_message(hs)) { + return ssl_hs_error; + } + + hs->tls13_state = state_process_client_finished; + return ssl_hs_read_message; +} + +static enum ssl_hs_wait_t do_process_client_finished(SSL_HANDSHAKE *hs) { + SSL *const ssl = hs->ssl; + if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) || + !tls13_process_finished(hs) || + !ssl_hash_current_message(hs) || + /* evp_aead_seal keys have already been switched. */ + !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_traffic_secret_0, + hs->hash_len) || + !tls13_derive_resumption_secret(hs)) { + return ssl_hs_error; + } + + ssl->method->received_flight(ssl); + + /* Rebase the session timestamp so that it is measured from ticket + * issuance. */ + ssl_session_rebase_time(ssl, hs->new_session); + hs->tls13_state = state_send_new_session_ticket; + return ssl_hs_ok; +} + +static enum ssl_hs_wait_t do_send_new_session_ticket(SSL_HANDSHAKE *hs) { + /* TLS 1.3 recommends single-use tickets, so issue multiple tickets in case the + * client makes several connections before getting a renewal. */ + static const int kNumTickets = 2; + + SSL *const ssl = hs->ssl; + /* If the client doesn't accept resumption with PSK_DHE_KE, don't send a + * session ticket. */ + if (!hs->accept_psk_mode) { + hs->tls13_state = state_done; + return ssl_hs_ok; + } + + SSL_SESSION *session = hs->new_session; + CBB cbb; + CBB_zero(&cbb); + + for (int i = 0; i < kNumTickets; i++) { + if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) { + goto err; + } + session->ticket_age_add_valid = 1; + + CBB body, ticket, extensions; + if (!ssl->method->init_message(ssl, &cbb, &body, + SSL3_MT_NEW_SESSION_TICKET) || + !CBB_add_u32(&body, session->timeout) || + !CBB_add_u32(&body, session->ticket_age_add) || + !CBB_add_u16_length_prefixed(&body, &ticket) || + !ssl_encrypt_ticket(ssl, &ticket, session) || + !CBB_add_u16_length_prefixed(&body, &extensions)) { + goto err; + } + + if (ssl->ctx->enable_early_data) { + session->ticket_max_early_data = kMaxEarlyDataAccepted; + + CBB early_data_info; + if (!CBB_add_u16(&extensions, TLSEXT_TYPE_ticket_early_data_info) || + !CBB_add_u16_length_prefixed(&extensions, &early_data_info) || + !CBB_add_u32(&early_data_info, session->ticket_max_early_data) || + !CBB_flush(&extensions)) { + goto err; + } + } + + /* Add a fake extension. See draft-davidben-tls-grease-01. */ + if (!CBB_add_u16(&extensions, + ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) || + !CBB_add_u16(&extensions, 0 /* empty */)) { + goto err; + } + + if (!ssl_add_message_cbb(ssl, &cbb)) { + goto err; + } + } + + hs->session_tickets_sent++; + hs->tls13_state = state_done; + return ssl_hs_flush; + +err: + CBB_cleanup(&cbb); + return ssl_hs_error; +} + +enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) { + while (hs->tls13_state != state_done) { + enum ssl_hs_wait_t ret = ssl_hs_error; + enum server_hs_state_t state = hs->tls13_state; + switch (state) { + case state_select_parameters: + ret = do_select_parameters(hs); + break; + case state_select_session: + ret = do_select_session(hs); + break; + case state_send_hello_retry_request: + ret = do_send_hello_retry_request(hs); + break; + case state_process_second_client_hello: + ret = do_process_second_client_hello(hs); + break; + case state_send_server_hello: + ret = do_send_server_hello(hs); + break; + case state_send_server_certificate_verify: + ret = do_send_server_certificate_verify(hs, 1 /* first run */); + break; + case state_complete_server_certificate_verify: + ret = do_send_server_certificate_verify(hs, 0 /* complete */); + break; + case state_send_server_finished: + ret = do_send_server_finished(hs); + break; + case state_process_client_certificate: + ret = do_process_client_certificate(hs); + break; + case state_process_client_certificate_verify: + ret = do_process_client_certificate_verify(hs); + break; + case state_process_channel_id: + ret = do_process_channel_id(hs); + break; + case state_process_client_finished: + ret = do_process_client_finished(hs); + break; + case state_send_new_session_ticket: + ret = do_send_new_session_ticket(hs); + break; + case state_done: + ret = ssl_hs_ok; + break; + } + + if (ret != ssl_hs_ok) { + return ret; + } + } + + return ssl_hs_ok; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls_method.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls_method.c new file mode 100644 index 000000000..6144f86c7 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls_method.c @@ -0,0 +1,328 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/buf.h> + +#include "../crypto/internal.h" +#include "internal.h" + + +static int ssl3_version_from_wire(uint16_t *out_version, + uint16_t wire_version) { + switch (wire_version) { + case SSL3_VERSION: + case TLS1_VERSION: + case TLS1_1_VERSION: + case TLS1_2_VERSION: + *out_version = wire_version; + return 1; + case TLS1_3_DRAFT_VERSION: + *out_version = TLS1_3_VERSION; + return 1; + } + + return 0; +} + +static uint16_t ssl3_version_to_wire(uint16_t version) { + switch (version) { + case SSL3_VERSION: + case TLS1_VERSION: + case TLS1_1_VERSION: + case TLS1_2_VERSION: + return version; + case TLS1_3_VERSION: + return TLS1_3_DRAFT_VERSION; + } + + /* It is an error to use this function with an invalid version. */ + assert(0); + return 0; +} + +static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; } + +static void ssl3_expect_flight(SSL *ssl) {} + +static void ssl3_received_flight(SSL *ssl) {} + +static int ssl3_set_read_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) { + if (ssl->s3->rrec.length != 0) { + /* There may not be unprocessed record data at a cipher change. */ + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFERED_MESSAGES_ON_CIPHER_CHANGE); + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + SSL_AEAD_CTX_free(aead_ctx); + return 0; + } + + OPENSSL_memset(ssl->s3->read_sequence, 0, sizeof(ssl->s3->read_sequence)); + + SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx); + ssl->s3->aead_read_ctx = aead_ctx; + return 1; +} + +static int ssl3_set_write_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) { + OPENSSL_memset(ssl->s3->write_sequence, 0, sizeof(ssl->s3->write_sequence)); + + SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx); + ssl->s3->aead_write_ctx = aead_ctx; + return 1; +} + +static const SSL_PROTOCOL_METHOD kTLSProtocolMethod = { + 0 /* is_dtls */, + SSL3_VERSION, + TLS1_3_VERSION, + ssl3_version_from_wire, + ssl3_version_to_wire, + ssl3_new, + ssl3_free, + ssl3_get_message, + ssl3_get_current_message, + ssl3_release_current_message, + ssl3_read_app_data, + ssl3_read_change_cipher_spec, + ssl3_read_close_notify, + ssl3_write_app_data, + ssl3_dispatch_alert, + ssl3_supports_cipher, + ssl3_init_message, + ssl3_finish_message, + ssl3_add_message, + ssl3_add_change_cipher_spec, + ssl3_add_alert, + ssl3_flush_flight, + ssl3_expect_flight, + ssl3_received_flight, + ssl3_set_read_state, + ssl3_set_write_state, +}; + +const SSL_METHOD *TLS_method(void) { + static const SSL_METHOD kMethod = { + 0, + &kTLSProtocolMethod, + &ssl_crypto_x509_method, + }; + return &kMethod; +} + +const SSL_METHOD *SSLv23_method(void) { + return TLS_method(); +} + +/* Legacy version-locked methods. */ + +const SSL_METHOD *TLSv1_2_method(void) { + static const SSL_METHOD kMethod = { + TLS1_2_VERSION, + &kTLSProtocolMethod, + &ssl_crypto_x509_method, + }; + return &kMethod; +} + +const SSL_METHOD *TLSv1_1_method(void) { + static const SSL_METHOD kMethod = { + TLS1_1_VERSION, + &kTLSProtocolMethod, + &ssl_crypto_x509_method, + }; + return &kMethod; +} + +const SSL_METHOD *TLSv1_method(void) { + static const SSL_METHOD kMethod = { + TLS1_VERSION, + &kTLSProtocolMethod, + &ssl_crypto_x509_method, + }; + return &kMethod; +} + +const SSL_METHOD *SSLv3_method(void) { + static const SSL_METHOD kMethod = { + SSL3_VERSION, + &kTLSProtocolMethod, + &ssl_crypto_x509_method, + }; + return &kMethod; +} + +/* Legacy side-specific methods. */ + +const SSL_METHOD *TLSv1_2_server_method(void) { + return TLSv1_2_method(); +} + +const SSL_METHOD *TLSv1_1_server_method(void) { + return TLSv1_1_method(); +} + +const SSL_METHOD *TLSv1_server_method(void) { + return TLSv1_method(); +} + +const SSL_METHOD *SSLv3_server_method(void) { + return SSLv3_method(); +} + +const SSL_METHOD *TLSv1_2_client_method(void) { + return TLSv1_2_method(); +} + +const SSL_METHOD *TLSv1_1_client_method(void) { + return TLSv1_1_method(); +} + +const SSL_METHOD *TLSv1_client_method(void) { + return TLSv1_method(); +} + +const SSL_METHOD *SSLv3_client_method(void) { + return SSLv3_method(); +} + +const SSL_METHOD *SSLv23_server_method(void) { + return SSLv23_method(); +} + +const SSL_METHOD *SSLv23_client_method(void) { + return SSLv23_method(); +} + +const SSL_METHOD *TLS_server_method(void) { + return TLS_method(); +} + +const SSL_METHOD *TLS_client_method(void) { + return TLS_method(); +} + +static int ssl_noop_x509_check_client_CA_names( + STACK_OF(CRYPTO_BUFFER) *names) { + return 1; +} + +static void ssl_noop_x509_clear(CERT *cert) {} +static void ssl_noop_x509_free(CERT *cert) {} +static void ssl_noop_x509_dup(CERT *new_cert, const CERT *cert) {} +static void ssl_noop_x509_flush_cached_leaf(CERT *cert) {} +static void ssl_noop_x509_flush_cached_chain(CERT *cert) {} +static int ssl_noop_x509_session_cache_objects(SSL_SESSION *sess) { + return 1; +} +static int ssl_noop_x509_session_dup(SSL_SESSION *new_session, + const SSL_SESSION *session) { + return 1; +} +static void ssl_noop_x509_session_clear(SSL_SESSION *session) {} +static int ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session, + SSL *ssl) { + if (!ssl->ctx->i_promise_to_verify_certs_after_the_handshake) { + ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNKNOWN_CA); + OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED); + return 0; + } + + session->verify_result = X509_V_OK; + return 1; +} + +static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {} +static int ssl_noop_x509_ssl_new(SSL *ctx) { return 1; } +static void ssl_noop_x509_ssl_free(SSL *ctx) { } +static void ssl_noop_x509_ssl_flush_cached_client_CA(SSL *ssl) {} +static int ssl_noop_x509_ssl_auto_chain_if_needed(SSL *ssl) { return 1; } +static int ssl_noop_x509_ssl_ctx_new(SSL_CTX *ctx) { return 1; } +static void ssl_noop_x509_ssl_ctx_free(SSL_CTX *ctx) { } +static void ssl_noop_x509_ssl_ctx_flush_cached_client_CA(SSL_CTX *ctx) {} + +static const SSL_X509_METHOD ssl_noop_x509_method = { + ssl_noop_x509_check_client_CA_names, + ssl_noop_x509_clear, + ssl_noop_x509_free, + ssl_noop_x509_dup, + ssl_noop_x509_flush_cached_chain, + ssl_noop_x509_flush_cached_leaf, + ssl_noop_x509_session_cache_objects, + ssl_noop_x509_session_dup, + ssl_noop_x509_session_clear, + ssl_noop_x509_session_verify_cert_chain, + ssl_noop_x509_hs_flush_cached_ca_names, + ssl_noop_x509_ssl_new, + ssl_noop_x509_ssl_free, + ssl_noop_x509_ssl_flush_cached_client_CA, + ssl_noop_x509_ssl_auto_chain_if_needed, + ssl_noop_x509_ssl_ctx_new, + ssl_noop_x509_ssl_ctx_free, + ssl_noop_x509_ssl_ctx_flush_cached_client_CA, +}; + +const SSL_METHOD *TLS_with_buffers_method(void) { + static const SSL_METHOD kMethod = { + 0, + &kTLSProtocolMethod, + &ssl_noop_x509_method, + }; + return &kMethod; +} diff --git a/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls_record.c b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls_record.c new file mode 100644 index 000000000..aafb6f513 --- /dev/null +++ b/roms/edk2/CryptoPkg/Library/OpensslLib/openssl/boringssl/ssl/tls_record.c @@ -0,0 +1,505 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ +/* ==================================================================== + * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ssl.h> + +#include <assert.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> +#include <openssl/mem.h> + +#include "internal.h" +#include "../crypto/internal.h" + + +/* kMaxEmptyRecords is the number of consecutive, empty records that will be + * processed. Without this limit an attacker could send empty records at a + * faster rate than we can process and cause record processing to loop + * forever. */ +static const uint8_t kMaxEmptyRecords = 32; + +/* kMaxEarlyDataSkipped is the maximum number of rejected early data bytes that + * will be skipped. Without this limit an attacker could send records at a + * faster rate than we can process and cause trial decryption to loop forever. + * This value should be slightly above kMaxEarlyDataAccepted in tls13_server.c, + * which is measured in plaintext. */ +static const size_t kMaxEarlyDataSkipped = 16384; + +/* kMaxWarningAlerts is the number of consecutive warning alerts that will be + * processed. */ +static const uint8_t kMaxWarningAlerts = 4; + +/* ssl_needs_record_splitting returns one if |ssl|'s current outgoing cipher + * state needs record-splitting and zero otherwise. */ +static int ssl_needs_record_splitting(const SSL *ssl) { + return ssl->s3->aead_write_ctx != NULL && + ssl->s3->aead_write_ctx->version < TLS1_1_VERSION && + (ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 && + SSL_CIPHER_is_block_cipher(ssl->s3->aead_write_ctx->cipher); +} + +int ssl_record_sequence_update(uint8_t *seq, size_t seq_len) { + for (size_t i = seq_len - 1; i < seq_len; i--) { + ++seq[i]; + if (seq[i] != 0) { + return 1; + } + } + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; +} + +size_t ssl_record_prefix_len(const SSL *ssl) { + size_t header_len; + if (SSL_is_dtls(ssl)) { + header_len = DTLS1_RT_HEADER_LENGTH; + } else { + header_len = SSL3_RT_HEADER_LENGTH; + } + + return header_len + SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_read_ctx); +} + +size_t ssl_seal_align_prefix_len(const SSL *ssl) { + if (SSL_is_dtls(ssl)) { + return DTLS1_RT_HEADER_LENGTH + + SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx); + } + + size_t ret = SSL3_RT_HEADER_LENGTH + + SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx); + if (ssl_needs_record_splitting(ssl)) { + ret += SSL3_RT_HEADER_LENGTH; + ret += ssl_cipher_get_record_split_len(ssl->s3->aead_write_ctx->cipher); + } + return ret; +} + +size_t SSL_max_seal_overhead(const SSL *ssl) { + if (SSL_is_dtls(ssl)) { + return dtls_max_seal_overhead(ssl, dtls1_use_current_epoch); + } + + size_t ret = SSL3_RT_HEADER_LENGTH; + ret += SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx); + /* TLS 1.3 needs an extra byte for the encrypted record type. */ + if (ssl->s3->aead_write_ctx != NULL && + ssl->s3->aead_write_ctx->version >= TLS1_3_VERSION) { + ret += 1; + } + if (ssl_needs_record_splitting(ssl)) { + ret *= 2; + } + return ret; +} + +enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, CBS *out, + size_t *out_consumed, uint8_t *out_alert, + uint8_t *in, size_t in_len) { + *out_consumed = 0; + + CBS cbs; + CBS_init(&cbs, in, in_len); + + /* Decode the record header. */ + uint8_t type; + uint16_t version, ciphertext_len; + if (!CBS_get_u8(&cbs, &type) || + !CBS_get_u16(&cbs, &version) || + !CBS_get_u16(&cbs, &ciphertext_len)) { + *out_consumed = SSL3_RT_HEADER_LENGTH; + return ssl_open_record_partial; + } + + int version_ok; + if (ssl->s3->aead_read_ctx == NULL) { + /* Only check the first byte. Enforcing beyond that can prevent decoding + * version negotiation failure alerts. */ + version_ok = (version >> 8) == SSL3_VERSION_MAJOR; + } else if (ssl3_protocol_version(ssl) < TLS1_3_VERSION) { + /* Earlier versions of TLS switch the record version. */ + version_ok = version == ssl->version; + } else { + /* Starting TLS 1.3, the version field is frozen at {3, 1}. */ + version_ok = version == TLS1_VERSION; + } + + if (!version_ok) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER); + *out_alert = SSL_AD_PROTOCOL_VERSION; + return ssl_open_record_error; + } + + /* Check the ciphertext length. */ + if (ciphertext_len > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_ENCRYPTED_LENGTH_TOO_LONG); + *out_alert = SSL_AD_RECORD_OVERFLOW; + return ssl_open_record_error; + } + + /* Extract the body. */ + CBS body; + if (!CBS_get_bytes(&cbs, &body, ciphertext_len)) { + *out_consumed = SSL3_RT_HEADER_LENGTH + (size_t)ciphertext_len; + return ssl_open_record_partial; + } + + ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HEADER, in, + SSL3_RT_HEADER_LENGTH); + + *out_consumed = in_len - CBS_len(&cbs); + + /* Skip early data received when expecting a second ClientHello if we rejected + * 0RTT. */ + if (ssl->s3->skip_early_data && + ssl->s3->aead_read_ctx == NULL && + type == SSL3_RT_APPLICATION_DATA) { + goto skipped_data; + } + + /* Decrypt the body in-place. */ + if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, type, version, + ssl->s3->read_sequence, (uint8_t *)CBS_data(&body), + CBS_len(&body))) { + if (ssl->s3->skip_early_data && + ssl->s3->aead_read_ctx != NULL) { + ERR_clear_error(); + goto skipped_data; + } + + OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC); + *out_alert = SSL_AD_BAD_RECORD_MAC; + return ssl_open_record_error; + } + + ssl->s3->skip_early_data = 0; + + if (!ssl_record_sequence_update(ssl->s3->read_sequence, 8)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return ssl_open_record_error; + } + + /* TLS 1.3 hides the record type inside the encrypted data. */ + if (ssl->s3->aead_read_ctx != NULL && + ssl->s3->aead_read_ctx->version >= TLS1_3_VERSION) { + /* The outer record type is always application_data. */ + if (type != SSL3_RT_APPLICATION_DATA) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_OUTER_RECORD_TYPE); + *out_alert = SSL_AD_DECODE_ERROR; + return ssl_open_record_error; + } + + do { + if (!CBS_get_last_u8(out, &type)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC); + *out_alert = SSL_AD_DECRYPT_ERROR; + return ssl_open_record_error; + } + } while (type == 0); + } + + /* Check the plaintext length. */ + if (CBS_len(out) > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG); + *out_alert = SSL_AD_RECORD_OVERFLOW; + return ssl_open_record_error; + } + + /* Limit the number of consecutive empty records. */ + if (CBS_len(out) == 0) { + ssl->s3->empty_record_count++; + if (ssl->s3->empty_record_count > kMaxEmptyRecords) { + OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_EMPTY_FRAGMENTS); + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; + } + /* Apart from the limit, empty records are returned up to the caller. This + * allows the caller to reject records of the wrong type. */ + } else { + ssl->s3->empty_record_count = 0; + } + + if (type == SSL3_RT_ALERT) { + return ssl_process_alert(ssl, out_alert, CBS_data(out), CBS_len(out)); + } + + ssl->s3->warning_alert_count = 0; + + *out_type = type; + return ssl_open_record_success; + +skipped_data: + ssl->s3->early_data_skipped += *out_consumed; + if (ssl->s3->early_data_skipped < *out_consumed) { + ssl->s3->early_data_skipped = kMaxEarlyDataSkipped + 1; + } + + if (ssl->s3->early_data_skipped > kMaxEarlyDataSkipped) { + OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA); + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + return ssl_open_record_error; + } + + return ssl_open_record_discard; +} + +static int do_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, + size_t max_out, uint8_t type, const uint8_t *in, + size_t in_len) { + assert(!buffers_alias(in, in_len, out, max_out)); + + /* TLS 1.3 hides the actual record type inside the encrypted data. */ + if (ssl->s3->aead_write_ctx != NULL && + ssl->s3->aead_write_ctx->version >= TLS1_3_VERSION) { + if (in_len > in_len + SSL3_RT_HEADER_LENGTH + 1 || + max_out < in_len + SSL3_RT_HEADER_LENGTH + 1) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + + OPENSSL_memcpy(out + SSL3_RT_HEADER_LENGTH, in, in_len); + out[SSL3_RT_HEADER_LENGTH + in_len] = type; + in = out + SSL3_RT_HEADER_LENGTH; + type = SSL3_RT_APPLICATION_DATA; + in_len++; + } + + if (max_out < SSL3_RT_HEADER_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL); + return 0; + } + + /* The TLS record-layer version number is meaningless and, starting in + * TLS 1.3, is frozen at TLS 1.0. But for historical reasons, SSL 3.0 + * ClientHellos should use SSL 3.0 and pre-TLS-1.3 expects the version + * to change after version negotiation. */ + uint16_t wire_version = TLS1_VERSION; + if (ssl->version == SSL3_VERSION || + (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION)) { + wire_version = ssl->version; + } + + /* Write the non-length portions of the header. */ + out[0] = type; + out[1] = wire_version >> 8; + out[2] = wire_version & 0xff; + out += 3; + max_out -= 3; + + /* Write the ciphertext, leaving two bytes for the length. */ + size_t ciphertext_len; + if (!SSL_AEAD_CTX_seal(ssl->s3->aead_write_ctx, out + 2, &ciphertext_len, + max_out - 2, type, wire_version, + ssl->s3->write_sequence, in, in_len) || + !ssl_record_sequence_update(ssl->s3->write_sequence, 8)) { + return 0; + } + + /* Fill in the length. */ + if (ciphertext_len >= 1 << 15) { + OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW); + return 0; + } + out[0] = ciphertext_len >> 8; + out[1] = ciphertext_len & 0xff; + + *out_len = SSL3_RT_HEADER_LENGTH + ciphertext_len; + + ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HEADER, out, + SSL3_RT_HEADER_LENGTH); + return 1; +} + +int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out, + uint8_t type, const uint8_t *in, size_t in_len) { + if (buffers_alias(in, in_len, out, max_out)) { + OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT); + return 0; + } + + size_t frag_len = 0; + if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 && + ssl_needs_record_splitting(ssl)) { + if (!do_seal_record(ssl, out, &frag_len, max_out, type, in, 1)) { + return 0; + } + in++; + in_len--; + out += frag_len; + max_out -= frag_len; + +#if !defined(BORINGSSL_UNSAFE_FUZZER_MODE) + assert(SSL3_RT_HEADER_LENGTH + ssl_cipher_get_record_split_len( + ssl->s3->aead_write_ctx->cipher) == + frag_len); +#endif + } + + if (!do_seal_record(ssl, out, out_len, max_out, type, in, in_len)) { + return 0; + } + *out_len += frag_len; + return 1; +} + +enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert, + const uint8_t *in, size_t in_len) { + /* Alerts records may not contain fragmented or multiple alerts. */ + if (in_len != 2) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT); + return ssl_open_record_error; + } + + ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_ALERT, in, in_len); + + const uint8_t alert_level = in[0]; + const uint8_t alert_descr = in[1]; + + uint16_t alert = (alert_level << 8) | alert_descr; + ssl_do_info_callback(ssl, SSL_CB_READ_ALERT, alert); + + if (alert_level == SSL3_AL_WARNING) { + if (alert_descr == SSL_AD_CLOSE_NOTIFY) { + ssl->s3->recv_shutdown = ssl_shutdown_close_notify; + return ssl_open_record_close_notify; + } + + /* Warning alerts do not exist in TLS 1.3. */ + if (ssl->s3->have_version && + ssl3_protocol_version(ssl) >= TLS1_3_VERSION) { + *out_alert = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT); + return ssl_open_record_error; + } + + ssl->s3->warning_alert_count++; + if (ssl->s3->warning_alert_count > kMaxWarningAlerts) { + *out_alert = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_WARNING_ALERTS); + return ssl_open_record_error; + } + return ssl_open_record_discard; + } + + if (alert_level == SSL3_AL_FATAL) { + ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert; + + char tmp[16]; + OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr); + BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr); + ERR_add_error_data(2, "SSL alert number ", tmp); + return ssl_open_record_fatal_alert; + } + + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_ALERT_TYPE); + return ssl_open_record_error; +} |