diff options
author | Adam Langley <agl@google.com> | 2015-01-22 14:27:53 -0800 |
---|---|---|
committer | Adam Langley <agl@google.com> | 2015-01-30 16:52:14 -0800 |
commit | d9e397b599b13d642138480a28c14db7a136bf05 (patch) | |
tree | 34bab61dc4ce323b123ad4614dbc07e86ea2f9ef /src/ssl | |
download | external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.zip external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.gz external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.bz2 |
Initial commit of BoringSSL for Android.
Diffstat (limited to 'src/ssl')
62 files changed, 42559 insertions, 0 deletions
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt new file mode 100644 index 0000000..91bd5ea --- /dev/null +++ b/src/ssl/CMakeLists.txt @@ -0,0 +1,45 @@ +include_directories(. .. ../include) + +add_subdirectory(pqueue) + +add_library( + ssl + + d1_both.c + d1_clnt.c + d1_lib.c + d1_meth.c + d1_pkt.c + d1_srtp.c + d1_srvr.c + s3_both.c + s3_clnt.c + s3_enc.c + s3_lib.c + s3_meth.c + s3_pkt.c + s3_srvr.c + ssl_algs.c + ssl_asn1.c + ssl_cert.c + ssl_ciph.c + ssl_error.c + ssl_lib.c + ssl_rsa.c + ssl_sess.c + ssl_stat.c + ssl_txt.c + t1_enc.c + t1_lib.c + t1_reneg.c + + $<TARGET_OBJECTS:pqueue> +) + +add_executable( + ssl_test + + ssl_test.c +) + +target_link_libraries(ssl_test ssl crypto) diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c new file mode 100644 index 0000000..5edc93f --- /dev/null +++ b/src/ssl/d1_both.c @@ -0,0 +1,1173 @@ +/* + * 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 <assert.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/rand.h> +#include <openssl/x509.h> + +#include "ssl_locl.h" + +#define RSMBLY_BITMASK_SIZE(msg_len) (((msg_len) + 7) / 8) + +#define RSMBLY_BITMASK_MARK(bitmask, start, end) \ + { \ + if ((end) - (start) <= 8) { \ + long ii; \ + for (ii = (start); ii < (end); ii++) \ + bitmask[((ii) >> 3)] |= (1 << ((ii)&7)); \ + } else { \ + long ii; \ + bitmask[((start) >> 3)] |= bitmask_start_values[((start)&7)]; \ + for (ii = (((start) >> 3) + 1); ii < ((((end)-1)) >> 3); ii++) \ + bitmask[ii] = 0xff; \ + bitmask[(((end)-1) >> 3)] |= bitmask_end_values[((end)&7)]; \ + } \ + } + +#define RSMBLY_BITMASK_IS_COMPLETE(bitmask, msg_len, is_complete) \ + { \ + long ii; \ + assert((msg_len) > 0); \ + is_complete = 1; \ + if (bitmask[(((msg_len)-1) >> 3)] != bitmask_end_values[((msg_len)&7)]) \ + is_complete = 0; \ + if (is_complete) \ + for (ii = (((msg_len)-1) >> 3) - 1; ii >= 0; ii--) \ + if (bitmask[ii] != 0xff) { \ + is_complete = 0; \ + break; \ + } \ + } + +static const uint8_t bitmask_start_values[] = {0xff, 0xfe, 0xfc, 0xf8, + 0xf0, 0xe0, 0xc0, 0x80}; +static const uint8_t bitmask_end_values[] = {0xff, 0x01, 0x03, 0x07, + 0x0f, 0x1f, 0x3f, 0x7f}; + +/* 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; + +static void dtls1_fix_message_header(SSL *s, unsigned long frag_off, + unsigned long frag_len); +static unsigned char *dtls1_write_message_header(SSL *s, unsigned char *p); +static long dtls1_get_message_fragment(SSL *s, int stn, long max, int *ok); + +static hm_fragment *dtls1_hm_fragment_new(unsigned long frag_len, + int reassembly) { + hm_fragment *frag = NULL; + unsigned char *buf = NULL; + unsigned char *bitmask = NULL; + + frag = (hm_fragment *)OPENSSL_malloc(sizeof(hm_fragment)); + if (frag == NULL) { + return NULL; + } + + if (frag_len) { + buf = (unsigned char *)OPENSSL_malloc(frag_len); + if (buf == NULL) { + OPENSSL_free(frag); + return NULL; + } + } + + /* zero length fragment gets zero frag->fragment */ + frag->fragment = buf; + + /* Initialize reassembly bitmask if necessary */ + if (reassembly) { + bitmask = (unsigned char *)OPENSSL_malloc(RSMBLY_BITMASK_SIZE(frag_len)); + if (bitmask == NULL) { + if (buf != NULL) { + OPENSSL_free(buf); + } + OPENSSL_free(frag); + return NULL; + } + memset(bitmask, 0, RSMBLY_BITMASK_SIZE(frag_len)); + } + + frag->reassembly = bitmask; + + return frag; +} + +void dtls1_hm_fragment_free(hm_fragment *frag) { + if (frag->msg_header.is_ccs) { + /* TODO(davidben): Simplify aead_write_ctx ownership, probably by just + * forbidding DTLS renego. */ + SSL_AEAD_CTX *aead_write_ctx = + frag->msg_header.saved_retransmit_state.aead_write_ctx; + if (aead_write_ctx) { + EVP_AEAD_CTX_cleanup(&aead_write_ctx->ctx); + OPENSSL_free(aead_write_ctx); + } + } + if (frag->fragment) { + OPENSSL_free(frag->fragment); + } + if (frag->reassembly) { + OPENSSL_free(frag->reassembly); + } + OPENSSL_free(frag); +} + +/* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or + * SSL3_RT_CHANGE_CIPHER_SPEC) */ +int dtls1_do_write(SSL *s, int type) { + int ret; + int curr_mtu; + unsigned int len, frag_off; + size_t max_overhead = 0; + + /* AHA! Figure out the MTU, and stick to the right size */ + if (s->d1->mtu < dtls1_min_mtu() && + !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)) { + long mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL); + if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) { + s->d1->mtu = (unsigned)mtu; + } else { + s->d1->mtu = kDefaultMTU; + BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_SET_MTU, s->d1->mtu, NULL); + } + } + + /* should have something reasonable now */ + assert(s->d1->mtu >= dtls1_min_mtu()); + + if (s->init_off == 0 && type == SSL3_RT_HANDSHAKE) { + assert(s->init_num == + (int)s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH); + } + + /* Determine the maximum overhead of the current cipher. */ + if (s->aead_write_ctx != NULL) { + max_overhead = EVP_AEAD_max_overhead(s->aead_write_ctx->ctx.aead); + if (s->aead_write_ctx->variable_nonce_included_in_record) { + max_overhead += s->aead_write_ctx->variable_nonce_len; + } + } + + frag_off = 0; + while (s->init_num) { + /* Account for data in the buffering BIO; multiple records may be packed + * into a single packet during the handshake. + * + * TODO(davidben): This is buggy; if the MTU is larger than the buffer size, + * the large record will be split across two packets. Moreover, in that + * case, the |dtls1_write_bytes| call may not return synchronously. This + * will break on retry as the |s->init_off| and |s->init_num| adjustment + * will run a second time. */ + curr_mtu = s->d1->mtu - BIO_wpending(SSL_get_wbio(s)) - + DTLS1_RT_HEADER_LENGTH - max_overhead; + + if (curr_mtu <= DTLS1_HM_HEADER_LENGTH) { + /* Flush the buffer and continue with a fresh packet. + * + * TODO(davidben): If |BIO_flush| is not synchronous and requires multiple + * calls to |dtls1_do_write|, |frag_off| will be wrong. */ + ret = BIO_flush(SSL_get_wbio(s)); + if (ret <= 0) { + return ret; + } + assert(BIO_wpending(SSL_get_wbio(s)) == 0); + curr_mtu = s->d1->mtu - DTLS1_RT_HEADER_LENGTH - max_overhead; + } + + /* XDTLS: this function is too long. split out the CCS part */ + if (type == SSL3_RT_HANDSHAKE) { + /* If this isn't the first fragment, reserve space to prepend a new + * fragment header. This will override the body of a previous fragment. */ + if (s->init_off != 0) { + assert(s->init_off > DTLS1_HM_HEADER_LENGTH); + s->init_off -= DTLS1_HM_HEADER_LENGTH; + s->init_num += DTLS1_HM_HEADER_LENGTH; + } + + if (curr_mtu <= DTLS1_HM_HEADER_LENGTH) { + /* To make forward progress, the MTU must, at minimum, fit the handshake + * header and one byte of handshake body. */ + OPENSSL_PUT_ERROR(SSL, dtls1_do_write, SSL_R_MTU_TOO_SMALL); + return -1; + } + + if (s->init_num > curr_mtu) { + len = curr_mtu; + } else { + len = s->init_num; + } + assert(len >= DTLS1_HM_HEADER_LENGTH); + + dtls1_fix_message_header(s, frag_off, len - DTLS1_HM_HEADER_LENGTH); + dtls1_write_message_header( + s, (uint8_t *)&s->init_buf->data[s->init_off]); + } else { + assert(type == SSL3_RT_CHANGE_CIPHER_SPEC); + /* ChangeCipherSpec cannot be fragmented. */ + if (s->init_num > curr_mtu) { + OPENSSL_PUT_ERROR(SSL, dtls1_do_write, SSL_R_MTU_TOO_SMALL); + return -1; + } + len = s->init_num; + } + + ret = dtls1_write_bytes(s, type, &s->init_buf->data[s->init_off], len); + if (ret < 0) { + return -1; + } + + /* bad if this assert fails, only part of the handshake message got sent. + * But why would this happen? */ + assert(len == (unsigned int)ret); + + if (ret == s->init_num) { + if (s->msg_callback) { + s->msg_callback(1, s->version, type, s->init_buf->data, + (size_t)(s->init_off + s->init_num), s, + s->msg_callback_arg); + } + + s->init_off = 0; /* done writing this message */ + s->init_num = 0; + + return 1; + } + s->init_off += ret; + s->init_num -= ret; + frag_off += (ret -= DTLS1_HM_HEADER_LENGTH); + } + + return 0; +} + + +/* Obtain handshake message of message type 'mt' (any if mt == -1), maximum + * acceptable body length 'max'. Read an entire handshake message. Handshake + * messages arrive in fragments. */ +long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, + int hash_message, int *ok) { + int i, al; + struct hm_header_st *msg_hdr; + uint8_t *p; + unsigned long msg_len; + + /* s3->tmp is used to store messages that are unexpected, caused + * by the absence of an optional handshake message */ + if (s->s3->tmp.reuse_message) { + /* A SSL_GET_MESSAGE_DONT_HASH_MESSAGE call cannot be combined + * with reuse_message; the SSL_GET_MESSAGE_DONT_HASH_MESSAGE + * would have to have been applied to the previous call. */ + assert(hash_message != SSL_GET_MESSAGE_DONT_HASH_MESSAGE); + s->s3->tmp.reuse_message = 0; + if (mt >= 0 && s->s3->tmp.message_type != mt) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_get_message, SSL_R_UNEXPECTED_MESSAGE); + goto f_err; + } + *ok = 1; + s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; + s->init_num = (int)s->s3->tmp.message_size; + return s->init_num; + } + + msg_hdr = &s->d1->r_msg_hdr; + memset(msg_hdr, 0x00, sizeof(struct hm_header_st)); + +again: + i = dtls1_get_message_fragment(s, stn, max, ok); + if (i == DTLS1_HM_BAD_FRAGMENT || + i == DTLS1_HM_FRAGMENT_RETRY) { + /* bad fragment received */ + goto again; + } else if (i <= 0 && !*ok) { + return i; + } + + p = (uint8_t *)s->init_buf->data; + msg_len = msg_hdr->msg_len; + + /* reconstruct message header */ + *(p++) = msg_hdr->type; + l2n3(msg_len, p); + s2n(msg_hdr->seq, p); + l2n3(0, p); + l2n3(msg_len, p); + p -= DTLS1_HM_HEADER_LENGTH; + msg_len += DTLS1_HM_HEADER_LENGTH; + + s->init_msg = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; + + if (hash_message != SSL_GET_MESSAGE_DONT_HASH_MESSAGE) { + ssl3_hash_current_message(s); + } + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, p, msg_len, s, + s->msg_callback_arg); + } + + memset(msg_hdr, 0x00, sizeof(struct hm_header_st)); + + s->d1->handshake_read_seq++; + + return s->init_num; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + *ok = 0; + return -1; +} + +static int dtls1_preprocess_fragment(SSL *s, struct hm_header_st *msg_hdr, + int max) { + size_t frag_off, frag_len, msg_len; + + msg_len = msg_hdr->msg_len; + frag_off = msg_hdr->frag_off; + frag_len = msg_hdr->frag_len; + + /* sanity checking */ + if ((frag_off + frag_len) > msg_len) { + OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, + SSL_R_EXCESSIVE_MESSAGE_SIZE); + return SSL_AD_ILLEGAL_PARAMETER; + } + + if ((frag_off + frag_len) > (unsigned long)max) { + OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, + SSL_R_EXCESSIVE_MESSAGE_SIZE); + return SSL_AD_ILLEGAL_PARAMETER; + } + + if (s->d1->r_msg_hdr.frag_off == 0) { + /* first fragment */ + /* msg_len is limited to 2^24, but is effectively checked + * against max above */ + if (!BUF_MEM_grow_clean(s->init_buf, msg_len + DTLS1_HM_HEADER_LENGTH)) { + OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, ERR_R_BUF_LIB); + return SSL_AD_INTERNAL_ERROR; + } + + s->s3->tmp.message_size = msg_len; + s->d1->r_msg_hdr.msg_len = msg_len; + s->s3->tmp.message_type = msg_hdr->type; + s->d1->r_msg_hdr.type = msg_hdr->type; + s->d1->r_msg_hdr.seq = msg_hdr->seq; + } else if (msg_len != s->d1->r_msg_hdr.msg_len) { + /* They must be playing with us! BTW, failure to enforce + * upper limit would open possibility for buffer overrun. */ + OPENSSL_PUT_ERROR(SSL, dtls1_preprocess_fragment, + SSL_R_EXCESSIVE_MESSAGE_SIZE); + return SSL_AD_ILLEGAL_PARAMETER; + } + + return 0; /* no error */ +} + + +static int dtls1_retrieve_buffered_fragment(SSL *s, long max, int *ok) { + /* (0) check whether the desired fragment is available + * if so: + * (1) copy over the fragment to s->init_buf->data[] + * (2) update s->init_num */ + pitem *item; + hm_fragment *frag; + int al; + unsigned long frag_len; + + *ok = 0; + item = pqueue_peek(s->d1->buffered_messages); + if (item == NULL) { + return 0; + } + + frag = (hm_fragment *)item->data; + + /* Don't return if reassembly still in progress */ + if (frag->reassembly != NULL) { + return 0; + } + + if (s->d1->handshake_read_seq != frag->msg_header.seq) { + return 0; + } + + frag_len = frag->msg_header.frag_len; + pqueue_pop(s->d1->buffered_messages); + + al = dtls1_preprocess_fragment(s, &frag->msg_header, max); + + if (al == 0) { + /* no alert */ + uint8_t *p = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; + memcpy(&p[frag->msg_header.frag_off], frag->fragment, + frag->msg_header.frag_len); + } + + dtls1_hm_fragment_free(frag); + pitem_free(item); + + if (al == 0) { + *ok = 1; + return frag_len; + } + + ssl3_send_alert(s, SSL3_AL_FATAL, al); + s->init_num = 0; + *ok = 0; + return -1; +} + +/* dtls1_max_handshake_message_len returns the maximum number of bytes + * permitted in a DTLS handshake message for |s|. The minimum is 16KB, but may + * be greater if the maximum certificate list size requires it. */ +static unsigned long dtls1_max_handshake_message_len(const SSL *s) { + unsigned long max_len = DTLS1_HM_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH; + if (max_len < (unsigned long)s->max_cert_list) { + return s->max_cert_list; + } + return max_len; +} + +static int dtls1_reassemble_fragment(SSL *s, const struct hm_header_st *msg_hdr, + int *ok) { + hm_fragment *frag = NULL; + pitem *item = NULL; + int i = -1, is_complete; + uint8_t seq64be[8]; + unsigned long frag_len = msg_hdr->frag_len; + + if ((msg_hdr->frag_off + frag_len) > msg_hdr->msg_len || + msg_hdr->msg_len > dtls1_max_handshake_message_len(s)) { + goto err; + } + + if (frag_len == 0) { + return DTLS1_HM_FRAGMENT_RETRY; + } + + /* Try to find item in queue */ + memset(seq64be, 0, sizeof(seq64be)); + seq64be[6] = (uint8_t)(msg_hdr->seq >> 8); + seq64be[7] = (uint8_t)msg_hdr->seq; + item = pqueue_find(s->d1->buffered_messages, seq64be); + + if (item == NULL) { + frag = dtls1_hm_fragment_new(msg_hdr->msg_len, 1); + if (frag == NULL) { + goto err; + } + memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr)); + frag->msg_header.frag_len = frag->msg_header.msg_len; + frag->msg_header.frag_off = 0; + } else { + frag = (hm_fragment *)item->data; + if (frag->msg_header.msg_len != msg_hdr->msg_len) { + item = NULL; + frag = NULL; + goto err; + } + } + + /* If message is already reassembled, this must be a + * retransmit and can be dropped. In this case item != NULL and so frag + * does not need to be freed. */ + if (frag->reassembly == NULL) { + uint8_t devnull[256]; + + assert(item != NULL); + while (frag_len) { + i = s->method->ssl_read_bytes( + s, SSL3_RT_HANDSHAKE, devnull, + frag_len > sizeof(devnull) ? sizeof(devnull) : frag_len, 0); + if (i <= 0) { + goto err; + } + frag_len -= i; + } + return DTLS1_HM_FRAGMENT_RETRY; + } + + /* read the body of the fragment (header has already been read */ + i = s->method->ssl_read_bytes( + s, SSL3_RT_HANDSHAKE, frag->fragment + msg_hdr->frag_off, frag_len, 0); + if ((unsigned long)i != frag_len) { + i = -1; + } + if (i <= 0) { + goto err; + } + + RSMBLY_BITMASK_MARK(frag->reassembly, (long)msg_hdr->frag_off, + (long)(msg_hdr->frag_off + frag_len)); + + RSMBLY_BITMASK_IS_COMPLETE(frag->reassembly, (long)msg_hdr->msg_len, + is_complete); + + if (is_complete) { + OPENSSL_free(frag->reassembly); + frag->reassembly = NULL; + } + + if (item == NULL) { + item = pitem_new(seq64be, frag); + if (item == NULL) { + i = -1; + goto err; + } + + item = pqueue_insert(s->d1->buffered_messages, item); + /* pqueue_insert fails iff a duplicate item is inserted. + * However, |item| cannot be a duplicate. If it were, + * |pqueue_find|, above, would have returned it and control + * would never have reached this branch. */ + assert(item != NULL); + } + + return DTLS1_HM_FRAGMENT_RETRY; + +err: + if (frag != NULL && item == NULL) { + dtls1_hm_fragment_free(frag); + } + *ok = 0; + return i; +} + +static int dtls1_process_out_of_seq_message(SSL *s, + const struct hm_header_st *msg_hdr, + int *ok) { + int i = -1; + hm_fragment *frag = NULL; + pitem *item = NULL; + uint8_t seq64be[8]; + unsigned long frag_len = msg_hdr->frag_len; + + if ((msg_hdr->frag_off + frag_len) > msg_hdr->msg_len) { + goto err; + } + + /* Try to find item in queue, to prevent duplicate entries */ + memset(seq64be, 0, sizeof(seq64be)); + seq64be[6] = (uint8_t)(msg_hdr->seq >> 8); + seq64be[7] = (uint8_t)msg_hdr->seq; + item = pqueue_find(s->d1->buffered_messages, seq64be); + + /* If we already have an entry and this one is a fragment, + * don't discard it and rather try to reassemble it. */ + if (item != NULL && frag_len != msg_hdr->msg_len) { + item = NULL; + } + + /* Discard the message if sequence number was already there, is + * too far in the future, already in the queue or if we received + * a FINISHED before the SERVER_HELLO, which then must be a stale + * retransmit. */ + if (msg_hdr->seq <= s->d1->handshake_read_seq || + msg_hdr->seq > s->d1->handshake_read_seq + 10 || item != NULL || + (s->d1->handshake_read_seq == 0 && msg_hdr->type == SSL3_MT_FINISHED)) { + uint8_t devnull[256]; + + while (frag_len) { + i = s->method->ssl_read_bytes( + s, SSL3_RT_HANDSHAKE, devnull, + frag_len > sizeof(devnull) ? sizeof(devnull) : frag_len, 0); + if (i <= 0) { + goto err; + } + frag_len -= i; + } + } else { + if (frag_len != msg_hdr->msg_len) { + return dtls1_reassemble_fragment(s, msg_hdr, ok); + } + + if (frag_len > dtls1_max_handshake_message_len(s)) { + goto err; + } + + frag = dtls1_hm_fragment_new(frag_len, 0); + if (frag == NULL) { + goto err; + } + + memcpy(&(frag->msg_header), msg_hdr, sizeof(*msg_hdr)); + + if (frag_len) { + /* read the body of the fragment (header has already been read */ + i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, frag->fragment, + frag_len, 0); + if ((unsigned long)i != frag_len) { + i = -1; + } + if (i <= 0) { + goto err; + } + } + + item = pitem_new(seq64be, frag); + if (item == NULL) { + goto err; + } + + item = pqueue_insert(s->d1->buffered_messages, item); + /* pqueue_insert fails iff a duplicate item is inserted. + * However, |item| cannot be a duplicate. If it were, + * |pqueue_find|, above, would have returned it. Then, either + * |frag_len| != |msg_hdr->msg_len| in which case |item| is set + * to NULL and it will have been processed with + * |dtls1_reassemble_fragment|, above, or the record will have + * been discarded. */ + assert(item != NULL); + } + + return DTLS1_HM_FRAGMENT_RETRY; + +err: + if (frag != NULL && item == NULL) { + dtls1_hm_fragment_free(frag); + } + *ok = 0; + return i; +} + + +static long dtls1_get_message_fragment(SSL *s, int stn, long max, int *ok) { + uint8_t wire[DTLS1_HM_HEADER_LENGTH]; + unsigned long len, frag_off, frag_len; + int i, al; + struct hm_header_st msg_hdr; + +redo: + /* see if we have the required fragment already */ + if ((frag_len = dtls1_retrieve_buffered_fragment(s, max, ok)) || *ok) { + if (*ok) { + s->init_num = frag_len; + } + return frag_len; + } + + /* read handshake message header */ + i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, wire, + DTLS1_HM_HEADER_LENGTH, 0); + if (i <= 0) { + /* nbio, or an error */ + s->rwstate = SSL_READING; + *ok = 0; + return i; + } + + /* Handshake fails if message header is incomplete */ + if (i != DTLS1_HM_HEADER_LENGTH) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment, + SSL_R_UNEXPECTED_MESSAGE); + goto f_err; + } + + /* parse the message fragment header */ + dtls1_get_message_header(wire, &msg_hdr); + + /* if this is a future (or stale) message it gets buffered + * (or dropped)--no further processing at this time. */ + if (msg_hdr.seq != s->d1->handshake_read_seq) { + return dtls1_process_out_of_seq_message(s, &msg_hdr, ok); + } + + len = msg_hdr.msg_len; + frag_off = msg_hdr.frag_off; + frag_len = msg_hdr.frag_len; + + if (frag_len && frag_len < len) { + return dtls1_reassemble_fragment(s, &msg_hdr, ok); + } + + if (!s->server && s->d1->r_msg_hdr.frag_off == 0 && + wire[0] == SSL3_MT_HELLO_REQUEST) { + /* The server may always send 'Hello Request' messages -- + * we are doing a handshake anyway now, so ignore them + * if their format is correct. Does not count for + * 'Finished' MAC. */ + if (wire[1] == 0 && wire[2] == 0 && wire[3] == 0) { + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, wire, + DTLS1_HM_HEADER_LENGTH, s, s->msg_callback_arg); + } + + s->init_num = 0; + goto redo; + } else { + /* Incorrectly formated Hello request */ + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment, + SSL_R_UNEXPECTED_MESSAGE); + goto f_err; + } + } + + if ((al = dtls1_preprocess_fragment(s, &msg_hdr, max))) { + goto f_err; + } + + /* XDTLS: ressurect this when restart is in place */ + s->state = stn; + + if (frag_len > 0) { + uint8_t *p = (uint8_t *)s->init_buf->data + DTLS1_HM_HEADER_LENGTH; + + i = s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &p[frag_off], frag_len, + 0); + /* XDTLS: fix this--message fragments cannot span multiple packets */ + if (i <= 0) { + s->rwstate = SSL_READING; + *ok = 0; + return i; + } + } else { + i = 0; + } + + /* XDTLS: an incorrectly formatted fragment should cause the + * handshake to fail */ + if (i != (int)frag_len) { + al = SSL3_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, dtls1_get_message_fragment, + SSL3_AD_ILLEGAL_PARAMETER); + goto f_err; + } + + *ok = 1; + + /* Note that s->init_num is *not* used as current offset in + * s->init_buf->data, but as a counter summing up fragments' + * lengths: as soon as they sum up to handshake packet + * length, we assume we have got all the fragments. */ + s->init_num = frag_len; + return frag_len; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + s->init_num = 0; + + *ok = 0; + return -1; +} + +/* for these 2 messages, we need to + * ssl->enc_read_ctx re-init + * ssl->s3->read_sequence zero + * ssl->s3->read_mac_secret re-init + * ssl->session->read_sym_enc assign + * ssl->session->read_compression assign + * ssl->session->read_hash assign */ +int dtls1_send_change_cipher_spec(SSL *s, int a, int b) { + uint8_t *p; + + if (s->state == a) { + p = (uint8_t *)s->init_buf->data; + *p++ = SSL3_MT_CCS; + s->d1->handshake_write_seq = s->d1->next_handshake_write_seq; + s->init_num = DTLS1_CCS_HEADER_LENGTH; + + s->init_off = 0; + + dtls1_set_message_header(s, SSL3_MT_CCS, 0, s->d1->handshake_write_seq, 0, + 0); + + /* buffer the message to handle re-xmits */ + dtls1_buffer_message(s, 1); + + s->state = b; + } + + /* SSL3_ST_CW_CHANGE_B */ + return dtls1_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC); +} + +int dtls1_read_failed(SSL *s, int code) { + if (code > 0) { + fprintf(stderr, "invalid state reached %s:%d", __FILE__, __LINE__); + return 1; + } + + if (!dtls1_is_timer_expired(s)) { + /* not a timeout, none of our business, let higher layers handle this. In + * fact, it's probably an error */ + return code; + } + + if (!SSL_in_init(s)) { + /* done, no need to send a retransmit */ + BIO_set_flags(SSL_get_rbio(s), BIO_FLAGS_READ); + return code; + } + + return dtls1_handle_timeout(s); +} + +int dtls1_get_queue_priority(unsigned short seq, int is_ccs) { + /* The index of the retransmission queue actually is the message sequence + * number, since the queue only contains messages of a single handshake. + * However, the ChangeCipherSpec has no message sequence number and so using + * only the sequence will result in the CCS and Finished having the same + * index. To prevent this, the sequence number is multiplied by 2. In case of + * a CCS 1 is subtracted. This does not only differ CSS and Finished, it also + * maintains the order of the index (important for priority queues) and fits + * in the unsigned short variable. */ + return seq * 2 - is_ccs; +} + +int dtls1_retransmit_buffered_messages(SSL *s) { + pqueue sent = s->d1->sent_messages; + piterator iter; + pitem *item; + hm_fragment *frag; + int found = 0; + + iter = pqueue_iterator(sent); + + for (item = pqueue_next(&iter); item != NULL; item = pqueue_next(&iter)) { + frag = (hm_fragment *)item->data; + if (dtls1_retransmit_message( + s, (unsigned short)dtls1_get_queue_priority( + frag->msg_header.seq, frag->msg_header.is_ccs), + 0, &found) <= 0 && + found) { + fprintf(stderr, "dtls1_retransmit_message() failed\n"); + return -1; + } + } + + return 1; +} + +int dtls1_buffer_message(SSL *s, int is_ccs) { + pitem *item; + hm_fragment *frag; + uint8_t seq64be[8]; + + /* this function is called immediately after a message has + * been serialized */ + assert(s->init_off == 0); + + frag = dtls1_hm_fragment_new(s->init_num, 0); + if (!frag) { + return 0; + } + + memcpy(frag->fragment, s->init_buf->data, s->init_num); + + if (is_ccs) { + assert(s->d1->w_msg_hdr.msg_len + DTLS1_CCS_HEADER_LENGTH == + (unsigned int)s->init_num); + } else { + assert(s->d1->w_msg_hdr.msg_len + DTLS1_HM_HEADER_LENGTH == + (unsigned int)s->init_num); + } + + frag->msg_header.msg_len = s->d1->w_msg_hdr.msg_len; + frag->msg_header.seq = s->d1->w_msg_hdr.seq; + frag->msg_header.type = s->d1->w_msg_hdr.type; + frag->msg_header.frag_off = 0; + frag->msg_header.frag_len = s->d1->w_msg_hdr.msg_len; + frag->msg_header.is_ccs = is_ccs; + + /* save current state*/ + frag->msg_header.saved_retransmit_state.aead_write_ctx = s->aead_write_ctx; + frag->msg_header.saved_retransmit_state.session = s->session; + frag->msg_header.saved_retransmit_state.epoch = s->d1->w_epoch; + + memset(seq64be, 0, sizeof(seq64be)); + seq64be[6] = (uint8_t)( + dtls1_get_queue_priority(frag->msg_header.seq, frag->msg_header.is_ccs) >> + 8); + seq64be[7] = (uint8_t)( + dtls1_get_queue_priority(frag->msg_header.seq, frag->msg_header.is_ccs)); + + item = pitem_new(seq64be, frag); + if (item == NULL) { + dtls1_hm_fragment_free(frag); + return 0; + } + + pqueue_insert(s->d1->sent_messages, item); + return 1; +} + +int dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off, + int *found) { + int ret; + /* XDTLS: for now assuming that read/writes are blocking */ + pitem *item; + hm_fragment *frag; + unsigned long header_length; + uint8_t seq64be[8]; + struct dtls1_retransmit_state saved_state; + uint8_t save_write_sequence[8]; + + /* assert(s->init_num == 0); + assert(s->init_off == 0); */ + + /* XDTLS: the requested message ought to be found, otherwise error */ + memset(seq64be, 0, sizeof(seq64be)); + seq64be[6] = (uint8_t)(seq >> 8); + seq64be[7] = (uint8_t)seq; + + item = pqueue_find(s->d1->sent_messages, seq64be); + if (item == NULL) { + fprintf(stderr, "retransmit: message %d non-existant\n", seq); + *found = 0; + return 0; + } + + *found = 1; + frag = (hm_fragment *)item->data; + + if (frag->msg_header.is_ccs) { + header_length = DTLS1_CCS_HEADER_LENGTH; + } else { + header_length = DTLS1_HM_HEADER_LENGTH; + } + + memcpy(s->init_buf->data, frag->fragment, + frag->msg_header.msg_len + header_length); + s->init_num = frag->msg_header.msg_len + header_length; + + dtls1_set_message_header(s, frag->msg_header.type, + frag->msg_header.msg_len, frag->msg_header.seq, + 0, frag->msg_header.frag_len); + + /* save current state */ + saved_state.aead_write_ctx = s->aead_write_ctx; + saved_state.session = s->session; + saved_state.epoch = s->d1->w_epoch; + + /* restore state in which the message was originally sent */ + s->aead_write_ctx = frag->msg_header.saved_retransmit_state.aead_write_ctx; + s->session = frag->msg_header.saved_retransmit_state.session; + s->d1->w_epoch = frag->msg_header.saved_retransmit_state.epoch; + + if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) { + memcpy(save_write_sequence, s->s3->write_sequence, + sizeof(s->s3->write_sequence)); + memcpy(s->s3->write_sequence, s->d1->last_write_sequence, + sizeof(s->s3->write_sequence)); + } + + ret = dtls1_do_write(s, frag->msg_header.is_ccs ? SSL3_RT_CHANGE_CIPHER_SPEC + : SSL3_RT_HANDSHAKE); + + /* restore current state */ + s->aead_write_ctx = saved_state.aead_write_ctx; + s->session = saved_state.session; + s->d1->w_epoch = saved_state.epoch; + + if (frag->msg_header.saved_retransmit_state.epoch == saved_state.epoch - 1) { + memcpy(s->d1->last_write_sequence, s->s3->write_sequence, + sizeof(s->s3->write_sequence)); + memcpy(s->s3->write_sequence, save_write_sequence, + sizeof(s->s3->write_sequence)); + } + + (void)BIO_flush(SSL_get_wbio(s)); + return ret; +} + +/* call this function when the buffered messages are no longer needed */ +void dtls1_clear_record_buffer(SSL *s) { + pitem *item; + + for (item = pqueue_pop(s->d1->sent_messages); item != NULL; + item = pqueue_pop(s->d1->sent_messages)) { + dtls1_hm_fragment_free((hm_fragment *)item->data); + pitem_free(item); + } +} + +/* don't actually do the writing, wait till the MTU has been retrieved */ +void dtls1_set_message_header(SSL *s, uint8_t mt, unsigned long len, + unsigned short seq_num, unsigned long frag_off, + unsigned long frag_len) { + struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr; + + msg_hdr->type = mt; + msg_hdr->msg_len = len; + msg_hdr->seq = seq_num; + msg_hdr->frag_off = frag_off; + msg_hdr->frag_len = frag_len; +} + +static void dtls1_fix_message_header(SSL *s, unsigned long frag_off, + unsigned long frag_len) { + struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr; + + msg_hdr->frag_off = frag_off; + msg_hdr->frag_len = frag_len; +} + +static uint8_t *dtls1_write_message_header(SSL *s, uint8_t *p) { + struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr; + + *p++ = msg_hdr->type; + l2n3(msg_hdr->msg_len, p); + + s2n(msg_hdr->seq, p); + l2n3(msg_hdr->frag_off, p); + l2n3(msg_hdr->frag_len, p); + + return p; +} + +unsigned int dtls1_min_mtu(void) { + return kMinMTU; +} + +void dtls1_get_message_header(uint8_t *data, + struct hm_header_st *msg_hdr) { + memset(msg_hdr, 0x00, sizeof(struct hm_header_st)); + msg_hdr->type = *(data++); + n2l3(data, msg_hdr->msg_len); + + n2s(data, msg_hdr->seq); + n2l3(data, msg_hdr->frag_off); + n2l3(data, msg_hdr->frag_len); +} + +void dtls1_get_ccs_header(uint8_t *data, struct ccs_header_st *ccs_hdr) { + memset(ccs_hdr, 0x00, sizeof(struct ccs_header_st)); + + ccs_hdr->type = *(data++); +} + +int dtls1_shutdown(SSL *s) { + int ret; + ret = ssl3_shutdown(s); + return ret; +} diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c new file mode 100644 index 0000000..3f9e814 --- /dev/null +++ b/src/ssl/d1_clnt.c @@ -0,0 +1,577 @@ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1999-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 (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 <assert.h> +#include <stdio.h> + +#include <openssl/bn.h> +#include <openssl/buf.h> +#include <openssl/dh.h> +#include <openssl/evp.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/rand.h> + +#include "ssl_locl.h" + +static int dtls1_get_hello_verify(SSL *s); + +int dtls1_connect(SSL *s) { + BUF_MEM *buf = NULL; + void (*cb)(const SSL *ssl, int type, int val) = NULL; + int ret = -1; + int new_state, state, skip = 0; + + assert(s->handshake_func == dtls1_connect); + assert(!s->server); + assert(SSL_IS_DTLS(s)); + + ERR_clear_error(); + ERR_clear_system_error(); + + if (s->info_callback != NULL) { + cb = s->info_callback; + } else if (s->ctx->info_callback != NULL) { + cb = s->ctx->info_callback; + } + + s->in_handshake++; + + for (;;) { + state = s->state; + + switch (s->state) { + case SSL_ST_RENEGOTIATE: + s->renegotiate = 1; + s->state = SSL_ST_CONNECT; + s->ctx->stats.sess_connect_renegotiate++; + /* break */ + case SSL_ST_CONNECT: + case SSL_ST_BEFORE | SSL_ST_CONNECT: + if (cb != NULL) { + cb(s, SSL_CB_HANDSHAKE_START, 1); + } + + if (s->init_buf == NULL) { + buf = BUF_MEM_new(); + if (buf == NULL || + !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { + ret = -1; + goto end; + } + s->init_buf = buf; + buf = NULL; + } + + if (!ssl3_setup_buffers(s) || + !ssl_init_wbio_buffer(s, 0)) { + ret = -1; + goto end; + } + + /* don't push the buffering BIO quite yet */ + + s->state = SSL3_ST_CW_CLNT_HELLO_A; + s->ctx->stats.sess_connect++; + s->init_num = 0; + s->d1->send_cookie = 0; + s->hit = 0; + break; + + case SSL3_ST_CW_CLNT_HELLO_A: + case SSL3_ST_CW_CLNT_HELLO_B: + s->shutdown = 0; + + /* every DTLS ClientHello resets Finished MAC */ + if (!ssl3_init_finished_mac(s)) { + OPENSSL_PUT_ERROR(SSL, dtls1_connect, ERR_R_INTERNAL_ERROR); + ret = -1; + goto end; + } + + dtls1_start_timer(s); + ret = ssl3_send_client_hello(s); + if (ret <= 0) { + goto end; + } + + if (s->d1->send_cookie) { + s->state = SSL3_ST_CW_FLUSH; + s->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A; + } else { + s->state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A; + } + + s->init_num = 0; + /* turn on buffering for the next lot of output */ + if (s->bbio != s->wbio) { + s->wbio = BIO_push(s->bbio, s->wbio); + } + + break; + + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B: + ret = dtls1_get_hello_verify(s); + if (ret <= 0) { + goto end; + } + if (s->d1->send_cookie) { + /* start again, with a cookie */ + dtls1_stop_timer(s); + s->state = SSL3_ST_CW_CLNT_HELLO_A; + } else { + s->state = SSL3_ST_CR_SRVR_HELLO_A; + } + s->init_num = 0; + break; + + case SSL3_ST_CR_SRVR_HELLO_A: + case SSL3_ST_CR_SRVR_HELLO_B: + ret = ssl3_get_server_hello(s); + if (ret <= 0) { + goto end; + } + + if (s->hit) { + s->state = SSL3_ST_CR_FINISHED_A; + if (s->tlsext_ticket_expected) { + /* receive renewed session ticket */ + s->state = SSL3_ST_CR_SESSION_TICKET_A; + } + } else { + s->state = SSL3_ST_CR_CERT_A; + } + s->init_num = 0; + break; + + case SSL3_ST_CR_CERT_A: + case SSL3_ST_CR_CERT_B: + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + ret = ssl3_get_server_certificate(s); + if (ret <= 0) { + goto end; + } + if (s->s3->tmp.certificate_status_expected) { + s->state = SSL3_ST_CR_CERT_STATUS_A; + } else { + s->state = SSL3_ST_CR_KEY_EXCH_A; + } + } else { + skip = 1; + s->state = SSL3_ST_CR_KEY_EXCH_A; + } + s->init_num = 0; + break; + + case SSL3_ST_CR_KEY_EXCH_A: + case SSL3_ST_CR_KEY_EXCH_B: + ret = ssl3_get_server_key_exchange(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_CERT_REQ_A; + s->init_num = 0; + + /* at this point we check that we have the + * required stuff from the server */ + if (!ssl3_check_cert_and_algorithm(s)) { + ret = -1; + goto end; + } + break; + + case SSL3_ST_CR_CERT_REQ_A: + case SSL3_ST_CR_CERT_REQ_B: + ret = ssl3_get_certificate_request(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_SRVR_DONE_A; + s->init_num = 0; + break; + + case SSL3_ST_CR_SRVR_DONE_A: + case SSL3_ST_CR_SRVR_DONE_B: + ret = ssl3_get_server_done(s); + if (ret <= 0) { + goto end; + } + dtls1_stop_timer(s); + if (s->s3->tmp.cert_req) { + s->s3->tmp.next_state = SSL3_ST_CW_CERT_A; + } else { + s->s3->tmp.next_state = SSL3_ST_CW_KEY_EXCH_A; + } + s->init_num = 0; + s->state = s->s3->tmp.next_state; + break; + + case SSL3_ST_CW_CERT_A: + case SSL3_ST_CW_CERT_B: + case SSL3_ST_CW_CERT_C: + case SSL3_ST_CW_CERT_D: + dtls1_start_timer(s); + ret = ssl3_send_client_certificate(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CW_KEY_EXCH_A; + s->init_num = 0; + break; + + case SSL3_ST_CW_KEY_EXCH_A: + case SSL3_ST_CW_KEY_EXCH_B: + dtls1_start_timer(s); + ret = ssl3_send_client_key_exchange(s); + if (ret <= 0) { + goto end; + } + /* For TLS, cert_req is set to 2, so a cert chain + * of nothing is sent, but no verify packet is sent */ + if (s->s3->tmp.cert_req == 1) { + s->state = SSL3_ST_CW_CERT_VRFY_A; + } else { + s->state = SSL3_ST_CW_CHANGE_A; + s->s3->change_cipher_spec = 0; + } + + s->init_num = 0; + break; + + case SSL3_ST_CW_CERT_VRFY_A: + case SSL3_ST_CW_CERT_VRFY_B: + dtls1_start_timer(s); + ret = ssl3_send_cert_verify(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CW_CHANGE_A; + s->init_num = 0; + s->s3->change_cipher_spec = 0; + break; + + case SSL3_ST_CW_CHANGE_A: + case SSL3_ST_CW_CHANGE_B: + if (!s->hit) { + dtls1_start_timer(s); + } + ret = dtls1_send_change_cipher_spec(s, SSL3_ST_CW_CHANGE_A, + SSL3_ST_CW_CHANGE_B); + if (ret <= 0) { + goto end; + } + + s->state = SSL3_ST_CW_FINISHED_A; + s->init_num = 0; + + s->session->cipher = s->s3->tmp.new_cipher; + if (!s->enc_method->setup_key_block(s) || + !s->enc_method->change_cipher_state( + s, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) { + ret = -1; + goto end; + } + + dtls1_reset_seq_numbers(s, SSL3_CC_WRITE); + break; + + case SSL3_ST_CW_FINISHED_A: + case SSL3_ST_CW_FINISHED_B: + if (!s->hit) { + dtls1_start_timer(s); + } + + ret = + ssl3_send_finished(s, SSL3_ST_CW_FINISHED_A, SSL3_ST_CW_FINISHED_B, + s->enc_method->client_finished_label, + s->enc_method->client_finished_label_len); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CW_FLUSH; + + if (s->hit) { + s->s3->tmp.next_state = SSL_ST_OK; + } else { + /* Allow NewSessionTicket if ticket expected */ + if (s->tlsext_ticket_expected) { + s->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A; + } else { + s->s3->tmp.next_state = SSL3_ST_CR_FINISHED_A; + } + } + s->init_num = 0; + break; + + case SSL3_ST_CR_SESSION_TICKET_A: + case SSL3_ST_CR_SESSION_TICKET_B: + ret = ssl3_get_new_session_ticket(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_FINISHED_A; + s->init_num = 0; + break; + + case SSL3_ST_CR_CERT_STATUS_A: + case SSL3_ST_CR_CERT_STATUS_B: + ret = ssl3_get_cert_status(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_KEY_EXCH_A; + s->init_num = 0; + break; + + case SSL3_ST_CR_FINISHED_A: + case SSL3_ST_CR_FINISHED_B: + s->d1->change_cipher_spec_ok = 1; + ret = + ssl3_get_finished(s, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B); + if (ret <= 0) { + goto end; + } + dtls1_stop_timer(s); + + if (s->hit) { + s->state = SSL3_ST_CW_CHANGE_A; + } else { + s->state = SSL_ST_OK; + } + + s->init_num = 0; + break; + + case SSL3_ST_CW_FLUSH: + s->rwstate = SSL_WRITING; + if (BIO_flush(s->wbio) <= 0) { + /* If the write error was fatal, stop trying */ + if (!BIO_should_retry(s->wbio)) { + s->rwstate = SSL_NOTHING; + s->state = s->s3->tmp.next_state; + } + + ret = -1; + goto end; + } + s->rwstate = SSL_NOTHING; + s->state = s->s3->tmp.next_state; + break; + + case SSL_ST_OK: + /* clean a few things up */ + ssl3_cleanup_key_block(s); + + /* Remove write buffering now. */ + ssl_free_wbio_buffer(s); + + s->init_num = 0; + s->renegotiate = 0; + s->new_session = 0; + + ssl_update_cache(s, SSL_SESS_CACHE_CLIENT); + if (s->hit) { + s->ctx->stats.sess_hit++; + } + + ret = 1; + s->ctx->stats.sess_connect_good++; + + if (cb != NULL) + cb(s, SSL_CB_HANDSHAKE_DONE, 1); + + /* done with handshaking */ + s->d1->handshake_read_seq = 0; + s->d1->next_handshake_write_seq = 0; + goto end; + + default: + OPENSSL_PUT_ERROR(SSL, dtls1_connect, SSL_R_UNKNOWN_STATE); + ret = -1; + goto end; + } + + /* did we do anything? */ + if (!s->s3->tmp.reuse_message && !skip) { + if ((cb != NULL) && (s->state != state)) { + new_state = s->state; + s->state = state; + cb(s, SSL_CB_CONNECT_LOOP, 1); + s->state = new_state; + } + } + skip = 0; + } + +end: + s->in_handshake--; + + if (buf != NULL) { + BUF_MEM_free(buf); + } + if (cb != NULL) { + cb(s, SSL_CB_CONNECT_EXIT, ret); + } + return ret; +} + +static int dtls1_get_hello_verify(SSL *s) { + long n; + int al, ok = 0; + CBS hello_verify_request, cookie; + uint16_t server_version; + + n = s->method->ssl_get_message( + s, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A, DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B, + -1, + /* Use the same maximum size as ssl3_get_server_hello. */ + 20000, SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + if (s->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) { + s->d1->send_cookie = 0; + s->s3->tmp.reuse_message = 1; + return 1; + } + + CBS_init(&hello_verify_request, s->init_msg, n); + + 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, ssl3_get_cert_status, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (CBS_len(&cookie) > sizeof(s->d1->cookie)) { + al = SSL_AD_ILLEGAL_PARAMETER; + goto f_err; + } + + memcpy(s->d1->cookie, CBS_data(&cookie), CBS_len(&cookie)); + s->d1->cookie_len = CBS_len(&cookie); + + s->d1->send_cookie = 1; + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + return -1; +} diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c new file mode 100644 index 0000000..8244cb9 --- /dev/null +++ b/src/ssl/d1_lib.c @@ -0,0 +1,439 @@ +/* + * 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/base.h> + +#include <limits.h> +#include <stdio.h> + +#if defined(OPENSSL_WINDOWS) +#include <sys/timeb.h> +#else +#include <sys/socket.h> +#include <sys/time.h> +#endif + +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/obj.h> + +#include "ssl_locl.h" + +static void get_current_time(OPENSSL_timeval *t); +static OPENSSL_timeval *dtls1_get_timeout(SSL *s, OPENSSL_timeval *timeleft); +static void dtls1_set_handshake_header(SSL *s, int type, unsigned long len); +static int dtls1_handshake_write(SSL *s); + +const SSL3_ENC_METHOD DTLSv1_enc_data = { + tls1_enc, + tls1_prf, + tls1_setup_key_block, + tls1_generate_master_secret, + tls1_change_cipher_state, + tls1_final_finish_mac, + TLS1_FINISH_MAC_LENGTH, + tls1_cert_verify_mac, + TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE, + TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE, + tls1_alert_code, + tls1_export_keying_material, + SSL_ENC_FLAG_DTLS|SSL_ENC_FLAG_EXPLICIT_IV, + DTLS1_HM_HEADER_LENGTH, + dtls1_set_handshake_header, + dtls1_handshake_write, +}; + +const SSL3_ENC_METHOD DTLSv1_2_enc_data = { + tls1_enc, + tls1_prf, + tls1_setup_key_block, + tls1_generate_master_secret, + tls1_change_cipher_state, + tls1_final_finish_mac, + TLS1_FINISH_MAC_LENGTH, + tls1_cert_verify_mac, + TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE, + TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE, + tls1_alert_code, + tls1_export_keying_material, + SSL_ENC_FLAG_DTLS | SSL_ENC_FLAG_EXPLICIT_IV | SSL_ENC_FLAG_SIGALGS | + SSL_ENC_FLAG_SHA256_PRF | SSL_ENC_FLAG_TLS1_2_CIPHERS, + DTLS1_HM_HEADER_LENGTH, + dtls1_set_handshake_header, + dtls1_handshake_write, +}; + +int dtls1_new(SSL *s) { + DTLS1_STATE *d1; + + if (!ssl3_new(s)) { + return 0; + } + d1 = OPENSSL_malloc(sizeof *d1); + if (d1 == NULL) { + ssl3_free(s); + return 0; + } + memset(d1, 0, sizeof *d1); + + d1->unprocessed_rcds.q = pqueue_new(); + d1->processed_rcds.q = pqueue_new(); + d1->buffered_messages = pqueue_new(); + d1->sent_messages = pqueue_new(); + d1->buffered_app_data.q = pqueue_new(); + + if (!d1->unprocessed_rcds.q || !d1->processed_rcds.q || + !d1->buffered_messages || !d1->sent_messages || + !d1->buffered_app_data.q) { + if (d1->unprocessed_rcds.q) { + pqueue_free(d1->unprocessed_rcds.q); + } + if (d1->processed_rcds.q) { + pqueue_free(d1->processed_rcds.q); + } + if (d1->buffered_messages) { + pqueue_free(d1->buffered_messages); + } + if (d1->sent_messages) { + pqueue_free(d1->sent_messages); + } + if (d1->buffered_app_data.q) { + pqueue_free(d1->buffered_app_data.q); + } + OPENSSL_free(d1); + ssl3_free(s); + return 0; + } + + s->d1 = d1; + + /* Set the version to the highest version for DTLS. This controls the initial + * state of |s->enc_method| and what the API reports as the version prior to + * negotiation. + * + * TODO(davidben): This is fragile and confusing. */ + s->version = DTLS1_2_VERSION; + return 1; +} + +static void dtls1_clear_queues(SSL *s) { + pitem *item = NULL; + hm_fragment *frag = NULL; + DTLS1_RECORD_DATA *rdata; + + while ((item = pqueue_pop(s->d1->unprocessed_rcds.q)) != NULL) { + rdata = (DTLS1_RECORD_DATA *)item->data; + if (rdata->rbuf.buf) { + OPENSSL_free(rdata->rbuf.buf); + } + OPENSSL_free(item->data); + pitem_free(item); + } + + while ((item = pqueue_pop(s->d1->processed_rcds.q)) != NULL) { + rdata = (DTLS1_RECORD_DATA *)item->data; + if (rdata->rbuf.buf) { + OPENSSL_free(rdata->rbuf.buf); + } + OPENSSL_free(item->data); + pitem_free(item); + } + + while ((item = pqueue_pop(s->d1->buffered_messages)) != NULL) { + frag = (hm_fragment *)item->data; + dtls1_hm_fragment_free(frag); + pitem_free(item); + } + + while ((item = pqueue_pop(s->d1->sent_messages)) != NULL) { + frag = (hm_fragment *)item->data; + dtls1_hm_fragment_free(frag); + pitem_free(item); + } + + while ((item = pqueue_pop(s->d1->buffered_app_data.q)) != NULL) { + rdata = (DTLS1_RECORD_DATA *)item->data; + if (rdata->rbuf.buf) { + OPENSSL_free(rdata->rbuf.buf); + } + OPENSSL_free(item->data); + pitem_free(item); + } +} + +void dtls1_free(SSL *s) { + ssl3_free(s); + + if (s == NULL || s->d1 == NULL) { + return; + } + + dtls1_clear_queues(s); + + pqueue_free(s->d1->unprocessed_rcds.q); + pqueue_free(s->d1->processed_rcds.q); + pqueue_free(s->d1->buffered_messages); + pqueue_free(s->d1->sent_messages); + pqueue_free(s->d1->buffered_app_data.q); + + OPENSSL_free(s->d1); + s->d1 = NULL; +} + +long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg) { + int ret = 0; + + switch (cmd) { + case DTLS_CTRL_GET_TIMEOUT: + if (dtls1_get_timeout(s, (OPENSSL_timeval *)parg) != NULL) { + ret = 1; + } + break; + + case DTLS_CTRL_HANDLE_TIMEOUT: + ret = dtls1_handle_timeout(s); + break; + + default: + ret = ssl3_ctrl(s, cmd, larg, parg); + break; + } + + return ret; +} + +const SSL_CIPHER *dtls1_get_cipher(unsigned int u) { + const SSL_CIPHER *ciph = ssl3_get_cipher(u); + /* DTLS does not support stream ciphers. */ + if (ciph == NULL || ciph->algorithm_enc == SSL_RC4) { + return NULL; + } + + return ciph; +} + +void dtls1_start_timer(SSL *s) { + /* If timer is not set, initialize duration with 1 second */ + if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0) { + s->d1->timeout_duration = 1; + } + + /* Set timeout to current time */ + get_current_time(&s->d1->next_timeout); + + /* Add duration to current time */ + s->d1->next_timeout.tv_sec += s->d1->timeout_duration; + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, + &s->d1->next_timeout); +} + +static OPENSSL_timeval *dtls1_get_timeout(SSL *s, OPENSSL_timeval *timeleft) { + OPENSSL_timeval timenow; + + /* If no timeout is set, just return NULL */ + if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0) { + return NULL; + } + + /* Get current time */ + get_current_time(&timenow); + + /* If timer already expired, set remaining time to 0 */ + if (s->d1->next_timeout.tv_sec < timenow.tv_sec || + (s->d1->next_timeout.tv_sec == timenow.tv_sec && + s->d1->next_timeout.tv_usec <= timenow.tv_usec)) { + memset(timeleft, 0, sizeof(OPENSSL_timeval)); + return timeleft; + } + + /* Calculate time left until timer expires */ + memcpy(timeleft, &s->d1->next_timeout, sizeof(OPENSSL_timeval)); + timeleft->tv_sec -= timenow.tv_sec; + timeleft->tv_usec -= timenow.tv_usec; + if (timeleft->tv_usec < 0) { + timeleft->tv_sec--; + timeleft->tv_usec += 1000000; + } + + /* If remaining time is less than 15 ms, set it to 0 to prevent issues + * because of small devergences with socket timeouts. */ + if (timeleft->tv_sec == 0 && timeleft->tv_usec < 15000) { + memset(timeleft, 0, sizeof(OPENSSL_timeval)); + } + + return timeleft; +} + +int dtls1_is_timer_expired(SSL *s) { + OPENSSL_timeval timeleft; + + /* Get time left until timeout, return false if no timer running */ + if (dtls1_get_timeout(s, &timeleft) == NULL) { + 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 *s) { + s->d1->timeout_duration *= 2; + if (s->d1->timeout_duration > 60) { + s->d1->timeout_duration = 60; + } + dtls1_start_timer(s); +} + +void dtls1_stop_timer(SSL *s) { + /* Reset everything */ + memset(&(s->d1->timeout), 0, sizeof(struct dtls1_timeout_st)); + memset(&s->d1->next_timeout, 0, sizeof(OPENSSL_timeval)); + s->d1->timeout_duration = 1; + BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, + &s->d1->next_timeout); + /* Clear retransmission buffer */ + dtls1_clear_record_buffer(s); +} + +int dtls1_check_timeout_num(SSL *s) { + s->d1->timeout.num_alerts++; + + /* Reduce MTU after 2 unsuccessful retransmissions */ + if (s->d1->timeout.num_alerts > 2 && + !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)) { + long mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, + NULL); + if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) { + s->d1->mtu = (unsigned)mtu; + } + } + + if (s->d1->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT) { + /* fail the connection, enough alerts have been sent */ + OPENSSL_PUT_ERROR(SSL, dtls1_check_timeout_num, SSL_R_READ_TIMEOUT_EXPIRED); + return -1; + } + + return 0; +} + +int dtls1_handle_timeout(SSL *s) { + /* if no timer is expired, don't do anything */ + if (!dtls1_is_timer_expired(s)) { + return 0; + } + + dtls1_double_timeout(s); + + if (dtls1_check_timeout_num(s) < 0) { + return -1; + } + + s->d1->timeout.read_timeouts++; + if (s->d1->timeout.read_timeouts > DTLS1_TMO_READ_COUNT) { + s->d1->timeout.read_timeouts = 1; + } + + dtls1_start_timer(s); + return dtls1_retransmit_buffered_messages(s); +} + +static void get_current_time(OPENSSL_timeval *t) { +#if defined(OPENSSL_WINDOWS) + struct _timeb time; + _ftime(&time); + t->tv_sec = time.time; + t->tv_usec = time.millitm * 1000; +#else + gettimeofday(t, NULL); +#endif +} + +static void dtls1_set_handshake_header(SSL *s, int htype, unsigned long len) { + uint8_t *message = (uint8_t *)s->init_buf->data; + const struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr; + uint8_t serialised_header[DTLS1_HM_HEADER_LENGTH]; + uint8_t *p = serialised_header; + + s->d1->handshake_write_seq = s->d1->next_handshake_write_seq; + s->d1->next_handshake_write_seq++; + + dtls1_set_message_header(s, htype, len, s->d1->handshake_write_seq, 0, len); + s->init_num = (int)len + DTLS1_HM_HEADER_LENGTH; + s->init_off = 0; + + /* Buffer the message to handle re-xmits */ + dtls1_buffer_message(s, 0); + + /* Add the new message to the handshake hash. Serialize the message + * header as if it were a single fragment. */ + *p++ = msg_hdr->type; + l2n3(msg_hdr->msg_len, p); + s2n(msg_hdr->seq, p); + l2n3(0, p); + l2n3(msg_hdr->msg_len, p); + ssl3_finish_mac(s, serialised_header, sizeof(serialised_header)); + ssl3_finish_mac(s, message + DTLS1_HM_HEADER_LENGTH, len); +} + +static int dtls1_handshake_write(SSL *s) { + return dtls1_do_write(s, SSL3_RT_HANDSHAKE); +} diff --git a/src/ssl/d1_meth.c b/src/ssl/d1_meth.c new file mode 100644 index 0000000..a894222 --- /dev/null +++ b/src/ssl/d1_meth.c @@ -0,0 +1,136 @@ +/* ssl/d1_meth.h */ +/* + * 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 "ssl_locl.h" + + +static const SSL_PROTOCOL_METHOD DTLS_protocol_method = { + dtls1_new, + dtls1_free, + dtls1_accept, + dtls1_connect, + ssl3_read, + ssl3_peek, + ssl3_write, + dtls1_shutdown, + ssl3_renegotiate, + ssl3_renegotiate_check, + dtls1_get_message, + dtls1_read_bytes, + dtls1_write_app_data_bytes, + dtls1_dispatch_alert, + dtls1_ctrl, + ssl3_ctx_ctrl, + ssl3_pending, + ssl3_num_ciphers, + dtls1_get_cipher, + ssl_undefined_void_function, + ssl3_callback_ctrl, + ssl3_ctx_callback_ctrl, +}; + +const SSL_METHOD *DTLS_method(void) { + static const SSL_METHOD method = { + 0, + &DTLS_protocol_method, + }; + return &method; +} + +/* Legacy version-locked methods. */ + +const SSL_METHOD *DTLSv1_2_method(void) { + static const SSL_METHOD method = { + DTLS1_2_VERSION, + &DTLS_protocol_method, + }; + return &method; +} + +const SSL_METHOD *DTLSv1_method(void) { + static const SSL_METHOD method = { + DTLS1_VERSION, + &DTLS_protocol_method, + }; + return &method; +} + +/* 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/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c new file mode 100644 index 0000000..a77ad4e --- /dev/null +++ b/src/ssl/d1_pkt.c @@ -0,0 +1,1321 @@ +/* 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 <stdio.h> +#include <errno.h> +#include <assert.h> + +#include <openssl/buf.h> +#include <openssl/mem.h> +#include <openssl/evp.h> +#include <openssl/err.h> +#include <openssl/rand.h> + +#include "ssl_locl.h" + + +/* mod 128 saturating subtract of two 64-bit values in big-endian order */ +static int satsub64be(const uint8_t *v1, const uint8_t *v2) { + int ret, sat, brw, i; + + if (sizeof(long) == 8) { + do { + const union { + long one; + char little; + } is_endian = {1}; + long l; + + if (is_endian.little) { + break; + } + /* not reached on little-endians */ + /* following test is redundant, because input is + * always aligned, but I take no chances... */ + if (((size_t)v1 | (size_t)v2) & 0x7) { + break; + } + + l = *((long *)v1); + l -= *((long *)v2); + if (l > 128) { + return 128; + } else if (l < -128) { + return -128; + } else { + return (int)l; + } + } while (0); + } + + ret = (int)v1[7] - (int)v2[7]; + sat = 0; + brw = ret >> 8; /* brw is either 0 or -1 */ + if (ret & 0x80) { + for (i = 6; i >= 0; i--) { + brw += (int)v1[i] - (int)v2[i]; + sat |= ~brw; + brw >>= 8; + } + } else { + for (i = 6; i >= 0; i--) { + brw += (int)v1[i] - (int)v2[i]; + sat |= brw; + brw >>= 8; + } + } + brw <<= 8; /* brw is either 0 or -256 */ + + if (sat & 0xff) { + return brw | 0x80; + } else { + return brw + (ret & 0xFF); + } +} + +static int have_handshake_fragment(SSL *s, int type, uint8_t *buf, int len, + int peek); +static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap); +static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap); +static DTLS1_BITMAP *dtls1_get_bitmap(SSL *s, SSL3_RECORD *rr, + unsigned int *is_next_epoch); +static int dtls1_buffer_record(SSL *s, record_pqueue *q, + uint8_t *priority); +static int dtls1_process_record(SSL *s); +static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, + unsigned int len); + +/* copy buffered record into SSL structure */ +static int dtls1_copy_record(SSL *s, pitem *item) { + DTLS1_RECORD_DATA *rdata; + + rdata = (DTLS1_RECORD_DATA *)item->data; + + if (s->s3->rbuf.buf != NULL) { + OPENSSL_free(s->s3->rbuf.buf); + } + + s->packet = rdata->packet; + s->packet_length = rdata->packet_length; + memcpy(&(s->s3->rbuf), &(rdata->rbuf), sizeof(SSL3_BUFFER)); + memcpy(&(s->s3->rrec), &(rdata->rrec), sizeof(SSL3_RECORD)); + + /* Set proper sequence number for mac calculation */ + memcpy(&(s->s3->read_sequence[2]), &(rdata->packet[5]), 6); + + return 1; +} + +static int dtls1_buffer_record(SSL *s, record_pqueue *queue, + uint8_t *priority) { + DTLS1_RECORD_DATA *rdata; + pitem *item; + + /* Limit the size of the queue to prevent DOS attacks */ + if (pqueue_size(queue->q) >= 100) { + return 0; + } + + rdata = OPENSSL_malloc(sizeof(DTLS1_RECORD_DATA)); + item = pitem_new(priority, rdata); + if (rdata == NULL || item == NULL) { + if (rdata != NULL) { + OPENSSL_free(rdata); + } + if (item != NULL) { + pitem_free(item); + } + + OPENSSL_PUT_ERROR(SSL, dtls1_buffer_record, ERR_R_INTERNAL_ERROR); + return -1; + } + + rdata->packet = s->packet; + rdata->packet_length = s->packet_length; + memcpy(&(rdata->rbuf), &(s->s3->rbuf), sizeof(SSL3_BUFFER)); + memcpy(&(rdata->rrec), &(s->s3->rrec), sizeof(SSL3_RECORD)); + + item->data = rdata; + + s->packet = NULL; + s->packet_length = 0; + memset(&(s->s3->rbuf), 0, sizeof(SSL3_BUFFER)); + memset(&(s->s3->rrec), 0, sizeof(SSL3_RECORD)); + + if (!ssl3_setup_buffers(s)) { + goto internal_error; + } + + /* insert should not fail, since duplicates are dropped */ + if (pqueue_insert(queue->q, item) == NULL) { + goto internal_error; + } + + return 1; + +internal_error: + OPENSSL_PUT_ERROR(SSL, dtls1_buffer_record, ERR_R_INTERNAL_ERROR); + if (rdata->rbuf.buf != NULL) { + OPENSSL_free(rdata->rbuf.buf); + } + OPENSSL_free(rdata); + pitem_free(item); + return -1; +} + +static int dtls1_retrieve_buffered_record(SSL *s, record_pqueue *queue) { + pitem *item; + + item = pqueue_pop(queue->q); + if (item) { + dtls1_copy_record(s, item); + + OPENSSL_free(item->data); + pitem_free(item); + + return 1; + } + + return 0; +} + +/* retrieve a buffered record that belongs to the new epoch, i.e., not + * processed yet */ +#define dtls1_get_unprocessed_record(s) \ + dtls1_retrieve_buffered_record((s), &((s)->d1->unprocessed_rcds)) + +/* retrieve a buffered record that belongs to the current epoch, i.e., + * processed */ +#define dtls1_get_processed_record(s) \ + dtls1_retrieve_buffered_record((s), &((s)->d1->processed_rcds)) + +static int dtls1_process_buffered_records(SSL *s) { + pitem *item; + + item = pqueue_peek(s->d1->unprocessed_rcds.q); + if (item) { + /* Check if epoch is current. */ + if (s->d1->unprocessed_rcds.epoch != s->d1->r_epoch) { + return 1; /* Nothing to do. */ + } + + /* Process all the records. */ + while (pqueue_peek(s->d1->unprocessed_rcds.q)) { + dtls1_get_unprocessed_record(s); + if (!dtls1_process_record(s)) { + return 0; + } + if (dtls1_buffer_record(s, &(s->d1->processed_rcds), + s->s3->rrec.seq_num) < 0) { + return -1; + } + } + } + + /* sync epoch numbers once all the unprocessed records have been processed */ + s->d1->processed_rcds.epoch = s->d1->r_epoch; + s->d1->unprocessed_rcds.epoch = s->d1->r_epoch + 1; + + return 1; +} + +static int dtls1_process_record(SSL *s) { + int al; + SSL3_RECORD *rr; + + rr = &(s->s3->rrec); + + /* At this point, s->packet_length == SSL3_RT_HEADER_LNGTH + rr->length, and + * we have that many bytes in s->packet. */ + rr->input = &(s->packet[DTLS1_RT_HEADER_LENGTH]); + + /* ok, we can now read from 's->packet' data into 'rr' rr->input points at + * rr->length bytes, which need to be copied into rr->data by either the + * decryption or by the decompression When the data is 'copied' into the + * rr->data buffer, rr->input will be pointed at the new buffer */ + + /* We now have - encrypted [ MAC [ compressed [ plain ] ] ] rr->length bytes + * of encrypted compressed stuff. */ + + /* check is not needed I believe */ + if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + al = SSL_AD_RECORD_OVERFLOW; + OPENSSL_PUT_ERROR(SSL, dtls1_process_record, + SSL_R_ENCRYPTED_LENGTH_TOO_LONG); + goto f_err; + } + + /* decrypt in place in 'rr->input' */ + rr->data = rr->input; + + if (!s->enc_method->enc(s, 0)) { + /* Bad packets are silently dropped in DTLS. Clear the error queue of any + * errors decryption may have added. */ + ERR_clear_error(); + rr->length = 0; + s->packet_length = 0; + goto err; + } + + if (rr->length > SSL3_RT_MAX_PLAIN_LENGTH) { + al = SSL_AD_RECORD_OVERFLOW; + OPENSSL_PUT_ERROR(SSL, dtls1_process_record, SSL_R_DATA_LENGTH_TOO_LONG); + goto f_err; + } + + rr->off = 0; + /* So at this point the following is true + * ssl->s3->rrec.type is the type of record + * ssl->s3->rrec.length == number of bytes in record + * ssl->s3->rrec.off == offset to first valid byte + * ssl->s3->rrec.data == where to take bytes from, increment + * after use :-). */ + + /* we have pulled in a full packet so zero things */ + s->packet_length = 0; + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + +err: + return 0; +} + +/* Call this to get a new input record. + * It will return <= 0 if more data is needed, normally due to an error + * or non-blocking IO. + * When it finishes, one packet has been decoded and can be found in + * ssl->s3->rrec.type - is the type of record + * ssl->s3->rrec.data, - data + * ssl->s3->rrec.length, - number of bytes + * + * used only by dtls1_read_bytes */ +int dtls1_get_record(SSL *s) { + int ssl_major, ssl_minor; + int i, n; + SSL3_RECORD *rr; + unsigned char *p = NULL; + unsigned short version; + DTLS1_BITMAP *bitmap; + unsigned int is_next_epoch; + + rr = &(s->s3->rrec); + + /* The epoch may have changed. If so, process all the pending records. This + * is a non-blocking operation. */ + if (dtls1_process_buffered_records(s) < 0) { + return -1; + } + + /* If we're renegotiating, then there may be buffered records. */ + if (dtls1_get_processed_record(s)) { + return 1; + } + + /* get something from the wire */ +again: + /* check if we have the header */ + if ((s->rstate != SSL_ST_READ_BODY) || + (s->packet_length < DTLS1_RT_HEADER_LENGTH)) { + n = ssl3_read_n(s, DTLS1_RT_HEADER_LENGTH, s->s3->rbuf.len, 0); + /* read timeout is handled by dtls1_read_bytes */ + if (n <= 0) { + return n; /* error or non-blocking */ + } + + /* this packet contained a partial record, dump it */ + if (s->packet_length != DTLS1_RT_HEADER_LENGTH) { + s->packet_length = 0; + goto again; + } + + s->rstate = SSL_ST_READ_BODY; + + p = s->packet; + + if (s->msg_callback) { + s->msg_callback(0, 0, SSL3_RT_HEADER, p, DTLS1_RT_HEADER_LENGTH, s, + s->msg_callback_arg); + } + + /* Pull apart the header into the DTLS1_RECORD */ + rr->type = *(p++); + ssl_major = *(p++); + ssl_minor = *(p++); + version = (ssl_major << 8) | ssl_minor; + + /* sequence number is 64 bits, with top 2 bytes = epoch */ + n2s(p, rr->epoch); + + memcpy(&(s->s3->read_sequence[2]), p, 6); + p += 6; + + n2s(p, rr->length); + + /* Lets check version */ + if (s->s3->have_version) { + if (version != s->version) { + /* The record's version doesn't match, so silently drop it. + * + * TODO(davidben): This doesn't work. The DTLS record layer is not + * packet-based, so the remainder of the packet isn't dropped and we + * get a framing error. It's also unclear what it means to silently + * drop a record in a packet containing two records. */ + rr->length = 0; + s->packet_length = 0; + goto again; + } + } + + if ((version & 0xff00) != (s->version & 0xff00)) { + /* wrong version, silently discard record */ + rr->length = 0; + s->packet_length = 0; + goto again; + } + + if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) { + /* record too long, silently discard it */ + rr->length = 0; + s->packet_length = 0; + goto again; + } + + /* now s->rstate == SSL_ST_READ_BODY */ + } + + /* s->rstate == SSL_ST_READ_BODY, get and decode the data */ + + if (rr->length > s->packet_length - DTLS1_RT_HEADER_LENGTH) { + /* now s->packet_length == DTLS1_RT_HEADER_LENGTH */ + i = rr->length; + n = ssl3_read_n(s, i, i, 1); + if (n <= 0) { + return n; /* error or non-blocking io */ + } + + /* this packet contained a partial record, dump it */ + if (n != i) { + rr->length = 0; + s->packet_length = 0; + goto again; + } + + /* now n == rr->length, + * and s->packet_length == DTLS1_RT_HEADER_LENGTH + rr->length */ + } + s->rstate = SSL_ST_READ_HEADER; /* set state for later operations */ + + /* match epochs. NULL means the packet is dropped on the floor */ + bitmap = dtls1_get_bitmap(s, rr, &is_next_epoch); + if (bitmap == NULL) { + rr->length = 0; + s->packet_length = 0; /* dump this record */ + goto again; /* get another record */ + } + + /* Check whether this is a repeat, or aged record. */ + if (!dtls1_record_replay_check(s, bitmap)) { + rr->length = 0; + s->packet_length = 0; /* dump this record */ + goto again; /* get another record */ + } + + /* just read a 0 length packet */ + if (rr->length == 0) { + goto again; + } + + /* If this record is from the next epoch (either HM or ALERT), + * and a handshake is currently in progress, buffer it since it + * cannot be processed at this time. + */ + if (is_next_epoch) { + if (SSL_in_init(s) || s->in_handshake) { + if (dtls1_buffer_record(s, &(s->d1->unprocessed_rcds), rr->seq_num) < 0) { + return -1; + } + dtls1_record_bitmap_update(s, bitmap); /* Mark receipt of record. */ + } + rr->length = 0; + s->packet_length = 0; + goto again; + } + + if (!dtls1_process_record(s)) { + rr->length = 0; + s->packet_length = 0; /* dump this record */ + goto again; /* get another record */ + } + dtls1_record_bitmap_update(s, bitmap); /* Mark receipt of record. */ + + return 1; +} + +/* Return up to 'len' payload bytes received in 'type' records. + * 'type' is one of the following: + * + * - SSL3_RT_HANDSHAKE (when ssl3_get_message calls us) + * - SSL3_RT_APPLICATION_DATA (when ssl3_read calls us) + * - 0 (during a shutdown, no data has to be returned) + * + * If we don't have stored data to work from, read a SSL/TLS record first + * (possibly multiple records if we still don't have anything to return). + * + * This function must handle any surprises the peer may have for us, such as + * Alert records (e.g. close_notify), ChangeCipherSpec records (not really + * a surprise, but handled as if it were), or renegotiation requests. + * Also if record payloads contain fragments too small to process, we store + * them until there is enough for the respective protocol (the record protocol + * may use arbitrary fragmentation and even interleaving): + * Change cipher spec protocol + * just 1 byte needed, no need for keeping anything stored + * Alert protocol + * 2 bytes needed (AlertLevel, AlertDescription) + * Handshake protocol + * 4 bytes needed (HandshakeType, uint24 length) -- we just have + * to detect unexpected Client Hello and Hello Request messages + * here, anything else is handled by higher layers + * Application data protocol + * none of our business + */ +int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek) { + int al, i, j, ret; + unsigned int n; + SSL3_RECORD *rr; + void (*cb)(const SSL *ssl, int type2, int val) = NULL; + + if (s->s3->rbuf.buf == NULL && !ssl3_setup_buffers(s)) { + return -1; + } + + /* XXX: check what the second '&& type' is about */ + if ((type && (type != SSL3_RT_APPLICATION_DATA) && + (type != SSL3_RT_HANDSHAKE) && type) || + (peek && (type != SSL3_RT_APPLICATION_DATA))) { + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* check whether there's a handshake message (client hello?) waiting */ + ret = have_handshake_fragment(s, type, buf, len, peek); + if (ret) { + return ret; + } + + /* Now s->d1->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */ + + if (!s->in_handshake && SSL_in_init(s)) { + /* type == SSL3_RT_APPLICATION_DATA */ + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + +start: + s->rwstate = SSL_NOTHING; + + /* s->s3->rrec.type - is the type of record + * s->s3->rrec.data - data + * s->s3->rrec.off - offset into 'data' for next read + * s->s3->rrec.length - number of bytes. */ + rr = &s->s3->rrec; + + /* We are not handshaking and have no data yet, + * so process data buffered during the last handshake + * in advance, if any. + */ + if (s->state == SSL_ST_OK && rr->length == 0) { + pitem *item; + item = pqueue_pop(s->d1->buffered_app_data.q); + if (item) { + dtls1_copy_record(s, item); + + OPENSSL_free(item->data); + pitem_free(item); + } + } + + /* Check for timeout */ + if (dtls1_handle_timeout(s) > 0) { + goto start; + } + + /* get new packet if necessary */ + if (rr->length == 0 || s->rstate == SSL_ST_READ_BODY) { + ret = dtls1_get_record(s); + if (ret <= 0) { + ret = dtls1_read_failed(s, ret); + /* anything other than a timeout is an error */ + if (ret <= 0) { + return ret; + } else { + goto start; + } + } + } + + /* we now have a packet which can be read and processed */ + + /* |change_cipher_spec is set when we receive a ChangeCipherSpec and reset by + * ssl3_get_finished. */ + if (s->s3->change_cipher_spec && rr->type != SSL3_RT_HANDSHAKE) { + /* We now have application data between CCS and Finished. Most likely the + * packets were reordered on their way, so buffer the application data for + * later processing rather than dropping the connection. */ + if (dtls1_buffer_record(s, &(s->d1->buffered_app_data), rr->seq_num) < 0) { + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, ERR_R_INTERNAL_ERROR); + return -1; + } + rr->length = 0; + goto start; + } + + /* If the other end has shut down, throw anything we read away (even in + * 'peek' mode) */ + if (s->shutdown & SSL_RECEIVED_SHUTDOWN) { + rr->length = 0; + s->rwstate = SSL_NOTHING; + return 0; + } + + + if (type == rr->type) { /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */ + /* make sure that we are not getting application data when we + * are doing a handshake for the first time */ + if (SSL_in_init(s) && (type == SSL3_RT_APPLICATION_DATA) && + (s->aead_read_ctx == NULL)) { + /* TODO(davidben): Is this check redundant with the handshake_func + * check? */ + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_APP_DATA_IN_HANDSHAKE); + goto f_err; + } + + if (len <= 0) { + return len; + } + + if ((unsigned int)len > rr->length) { + n = rr->length; + } else { + n = (unsigned int)len; + } + + memcpy(buf, &(rr->data[rr->off]), n); + if (!peek) { + rr->length -= n; + rr->off += n; + if (rr->length == 0) { + s->rstate = SSL_ST_READ_HEADER; + rr->off = 0; + } + } + + return n; + } + + /* If we get here, then type != rr->type; if we have a handshake message, + * then it was unexpected (Hello Request or Client Hello). */ + + /* In case of record types for which we have 'fragment' storage, fill that so + * that we can process the data at a fixed place. */ + { + unsigned int k, dest_maxlen = 0; + uint8_t *dest = NULL; + unsigned int *dest_len = NULL; + + if (rr->type == SSL3_RT_HANDSHAKE) { + dest_maxlen = sizeof s->d1->handshake_fragment; + dest = s->d1->handshake_fragment; + dest_len = &s->d1->handshake_fragment_len; + } else if (rr->type == SSL3_RT_ALERT) { + dest_maxlen = sizeof(s->d1->alert_fragment); + dest = s->d1->alert_fragment; + dest_len = &s->d1->alert_fragment_len; + } + /* else it's a CCS message, or application data or wrong */ + else if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) { + /* Application data while renegotiating is allowed. Try again reading. */ + if (rr->type == SSL3_RT_APPLICATION_DATA) { + BIO *bio; + s->s3->in_read_app_data = 2; + bio = SSL_get_rbio(s); + s->rwstate = SSL_READING; + BIO_clear_retry_flags(bio); + BIO_set_retry_read(bio); + return -1; + } + + /* Not certain if this is the right error handling */ + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD); + goto f_err; + } + + if (dest_maxlen > 0) { + /* XDTLS: In a pathalogical case, the Client Hello + * may be fragmented--don't always expect dest_maxlen bytes */ + if (rr->length < dest_maxlen) { + s->rstate = SSL_ST_READ_HEADER; + rr->length = 0; + goto start; + } + + /* now move 'n' bytes: */ + for (k = 0; k < dest_maxlen; k++) { + dest[k] = rr->data[rr->off++]; + rr->length--; + } + *dest_len = dest_maxlen; + } + } + + /* s->d1->handshake_fragment_len == 12 iff rr->type == SSL3_RT_HANDSHAKE; + * s->d1->alert_fragment_len == 7 iff rr->type == SSL3_RT_ALERT. + * (Possibly rr is 'empty' now, i.e. rr->length may be 0.) */ + + /* If we are a client, check for an incoming 'Hello Request': */ + if (!s->server && s->d1->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH && + s->d1->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST && + s->session != NULL && s->session->cipher != NULL) { + s->d1->handshake_fragment_len = 0; + + if ((s->d1->handshake_fragment[1] != 0) || + (s->d1->handshake_fragment[2] != 0) || + (s->d1->handshake_fragment[3] != 0)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_HELLO_REQUEST); + goto f_err; + } + + /* no need to check sequence number on HELLO REQUEST messages */ + + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, + s->d1->handshake_fragment, 4, s, s->msg_callback_arg); + } + + if (SSL_is_init_finished(s) && !s->s3->renegotiate) { + s->d1->handshake_read_seq++; + s->new_session = 1; + ssl3_renegotiate(s); + if (ssl3_renegotiate_check(s)) { + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + } + + /* we either finished a handshake or ignored the request, now try again to + * obtain the (application) data we were asked for */ + goto start; + } + + if (s->d1->alert_fragment_len >= DTLS1_AL_HEADER_LENGTH) { + int alert_level = s->d1->alert_fragment[0]; + int alert_descr = s->d1->alert_fragment[1]; + + s->d1->alert_fragment_len = 0; + + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_ALERT, s->d1->alert_fragment, 2, s, + s->msg_callback_arg); + } + + if (s->info_callback != NULL) { + cb = s->info_callback; + } else if (s->ctx->info_callback != NULL) { + cb = s->ctx->info_callback; + } + + if (cb != NULL) { + j = (alert_level << 8) | alert_descr; + cb(s, SSL_CB_READ_ALERT, j); + } + + if (alert_level == 1) { /* warning */ + s->s3->warn_alert = alert_descr; + if (alert_descr == SSL_AD_CLOSE_NOTIFY) { + s->shutdown |= SSL_RECEIVED_SHUTDOWN; + return 0; + } + } else if (alert_level == 2) { /* fatal */ + char tmp[16]; + + s->rwstate = SSL_NOTHING; + s->s3->fatal_alert = alert_descr; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, + SSL_AD_REASON_OFFSET + alert_descr); + BIO_snprintf(tmp, sizeof tmp, "%d", alert_descr); + ERR_add_error_data(2, "SSL alert number ", tmp); + s->shutdown |= SSL_RECEIVED_SHUTDOWN; + SSL_CTX_remove_session(s->ctx, s->session); + return 0; + } else { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNKNOWN_ALERT_TYPE); + goto f_err; + } + + goto start; + } + + if (s->shutdown & SSL_SENT_SHUTDOWN) { + /* but we have not received a shutdown */ + s->rwstate = SSL_NOTHING; + rr->length = 0; + return 0; + } + + if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) { + struct ccs_header_st ccs_hdr; + unsigned int ccs_hdr_len = DTLS1_CCS_HEADER_LENGTH; + + dtls1_get_ccs_header(rr->data, &ccs_hdr); + + /* 'Change Cipher Spec' is just a single byte, so we know + * exactly what the record payload has to look like */ + /* XDTLS: check that epoch is consistent */ + if ((rr->length != ccs_hdr_len) || (rr->off != 0) || + (rr->data[0] != SSL3_MT_CCS)) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_BAD_CHANGE_CIPHER_SPEC); + goto f_err; + } + + rr->length = 0; + + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, 1, s, + s->msg_callback_arg); + } + + /* We can't process a CCS now, because previous handshake + * messages are still missing, so just drop it. + */ + if (!s->d1->change_cipher_spec_ok) { + goto start; + } + + s->d1->change_cipher_spec_ok = 0; + + s->s3->change_cipher_spec = 1; + if (!ssl3_do_change_cipher_spec(s)) { + goto err; + } + + /* do this whenever CCS is processed */ + dtls1_reset_seq_numbers(s, SSL3_CC_READ); + + goto start; + } + + /* Unexpected handshake message (Client Hello, or protocol violation) */ + if ((s->d1->handshake_fragment_len >= DTLS1_HM_HEADER_LENGTH) && + !s->in_handshake) { + struct hm_header_st msg_hdr; + + /* this may just be a stale retransmit */ + dtls1_get_message_header(rr->data, &msg_hdr); + if (rr->epoch != s->d1->r_epoch) { + rr->length = 0; + goto start; + } + + /* If we are server, we may have a repeated FINISHED of the client here, + * then retransmit our CCS and FINISHED. */ + if (msg_hdr.type == SSL3_MT_FINISHED) { + if (dtls1_check_timeout_num(s) < 0) { + return -1; + } + + dtls1_retransmit_buffered_messages(s); + rr->length = 0; + goto start; + } + + if ((s->state & SSL_ST_MASK) == SSL_ST_OK) { + s->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT; + s->renegotiate = 1; + s->new_session = 1; + } + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + + goto start; + } + + switch (rr->type) { + default: + /* TLS just ignores unknown message types */ + if (s->version == TLS1_VERSION) { + rr->length = 0; + goto start; + } + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD); + goto f_err; + + case SSL3_RT_CHANGE_CIPHER_SPEC: + case SSL3_RT_ALERT: + case SSL3_RT_HANDSHAKE: + /* we already handled all of these, with the possible exception of + * SSL3_RT_HANDSHAKE when s->in_handshake is set, but that should not + * happen when type != rr->type */ + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, ERR_R_INTERNAL_ERROR); + goto f_err; + + case SSL3_RT_APPLICATION_DATA: + /* At this point, we were expecting handshake data, but have application + * data. If the library was running inside ssl3_read() (i.e. + * in_read_app_data is set) and it makes sense to read application data + * at this point (session renegotiation not yet started), we will indulge + * it. */ + if (s->s3->in_read_app_data && (s->s3->total_renegotiations != 0) && + (((s->state & SSL_ST_CONNECT) && + (s->state >= SSL3_ST_CW_CLNT_HELLO_A) && + (s->state <= SSL3_ST_CR_SRVR_HELLO_A)) || + ((s->state & SSL_ST_ACCEPT) && + (s->state <= SSL3_ST_SW_HELLO_REQ_A) && + (s->state >= SSL3_ST_SR_CLNT_HELLO_A)))) { + s->s3->in_read_app_data = 2; + return -1; + } else { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, dtls1_read_bytes, SSL_R_UNEXPECTED_RECORD); + goto f_err; + } + } + + /* not reached */ + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); +err: + return -1; +} + +int dtls1_write_app_data_bytes(SSL *s, int type, const void *buf_, int len) { + int i; + + if (SSL_in_init(s) && !s->in_handshake) { + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, dtls1_write_app_data_bytes, + SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + + if (len > SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, dtls1_write_app_data_bytes, + SSL_R_DTLS_MESSAGE_TOO_BIG); + return -1; + } + + i = dtls1_write_bytes(s, type, buf_, len); + return i; +} + + +/* this only happens when a client hello is received and a handshake is + * started. */ +static int have_handshake_fragment(SSL *s, int type, uint8_t *buf, + int len, int peek) { + if (type == SSL3_RT_HANDSHAKE && s->d1->handshake_fragment_len > 0) { + /* (partially) satisfy request from storage */ + uint8_t *src = s->d1->handshake_fragment; + uint8_t *dst = buf; + unsigned int k, n; + + /* peek == 0 */ + n = 0; + while (len > 0 && s->d1->handshake_fragment_len > 0) { + *dst++ = *src++; + len--; + s->d1->handshake_fragment_len--; + n++; + } + /* move any remaining fragment bytes: */ + for (k = 0; k < s->d1->handshake_fragment_len; k++) { + s->d1->handshake_fragment[k] = *src++; + } + return n; + } + + return 0; +} + +/* Call this to write data in records of type 'type' It will return <= 0 if not + * all data has been sent or non-blocking IO. */ +int dtls1_write_bytes(SSL *s, int type, const void *buf, int len) { + int i; + + assert(len <= SSL3_RT_MAX_PLAIN_LENGTH); + s->rwstate = SSL_NOTHING; + i = do_dtls1_write(s, type, buf, len); + return i; +} + +static int do_dtls1_write(SSL *s, int type, const uint8_t *buf, + unsigned int len) { + uint8_t *p, *pseq; + int i; + int prefix_len = 0; + int eivlen = 0; + SSL3_RECORD *wr; + SSL3_BUFFER *wb; + + /* first check if there is a SSL3_BUFFER still being written + * out. This will happen with non blocking IO */ + if (s->s3->wbuf.left != 0) { + assert(0); /* XDTLS: want to see if we ever get here */ + return ssl3_write_pending(s, type, buf, len); + } + + /* If we have an alert to send, lets send it */ + if (s->s3->alert_dispatch) { + i = s->method->ssl_dispatch_alert(s); + if (i <= 0) { + return i; + } + /* if it went, fall through and send more stuff */ + } + + if (len == 0) { + return 0; + } + + wr = &(s->s3->wrec); + wb = &(s->s3->wbuf); + + p = wb->buf + prefix_len; + + /* write the header */ + + *(p++) = type & 0xff; + wr->type = type; + /* Special case: for hello verify request, client version 1.0 and + * we haven't decided which version to use yet send back using + * version 1.0 header: otherwise some clients will ignore it. + */ + if (!s->s3->have_version) { + *(p++) = DTLS1_VERSION >> 8; + *(p++) = DTLS1_VERSION & 0xff; + } else { + *(p++) = s->version >> 8; + *(p++) = s->version & 0xff; + } + + /* field where we are to write out packet epoch, seq num and len */ + pseq = p; + p += 10; + + /* Leave room for the variable nonce for AEADs which specify it explicitly. */ + if (s->aead_write_ctx != NULL && + s->aead_write_ctx->variable_nonce_included_in_record) { + eivlen = s->aead_write_ctx->variable_nonce_len; + } + + /* lets setup the record stuff. */ + wr->data = p + eivlen; /* make room for IV in case of CBC */ + wr->length = (int)len; + wr->input = (unsigned char *)buf; + + /* we now 'read' from wr->input, wr->length bytes into wr->data */ + memcpy(wr->data, wr->input, wr->length); + wr->input = wr->data; + + /* this is true regardless of mac size */ + wr->input = p; + wr->data = p; + wr->length += eivlen; + + if (!s->enc_method->enc(s, 1)) { + goto err; + } + + /* there's only one epoch between handshake and app data */ + s2n(s->d1->w_epoch, pseq); + + memcpy(pseq, &(s->s3->write_sequence[2]), 6); + pseq += 6; + s2n(wr->length, pseq); + + if (s->msg_callback) { + s->msg_callback(1, 0, SSL3_RT_HEADER, pseq - DTLS1_RT_HEADER_LENGTH, + DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg); + } + + /* we should now have wr->data pointing to the encrypted data, which is + * wr->length long */ + wr->type = type; /* not needed but helps for debugging */ + wr->length += DTLS1_RT_HEADER_LENGTH; + + ssl3_record_sequence_update(&(s->s3->write_sequence[0])); + + /* now let's set up wb */ + wb->left = prefix_len + wr->length; + wb->offset = 0; + + /* memorize arguments so that ssl3_write_pending can detect bad write retries + * later */ + s->s3->wpend_tot = len; + s->s3->wpend_buf = buf; + s->s3->wpend_type = type; + s->s3->wpend_ret = len; + + /* we now just need to write the buffer */ + return ssl3_write_pending(s, type, buf, len); + +err: + return -1; +} + +static int dtls1_record_replay_check(SSL *s, DTLS1_BITMAP *bitmap) { + int cmp; + unsigned int shift; + const uint8_t *seq = s->s3->read_sequence; + + cmp = satsub64be(seq, bitmap->max_seq_num); + if (cmp > 0) { + memcpy(s->s3->rrec.seq_num, seq, 8); + return 1; /* this record in new */ + } + shift = -cmp; + if (shift >= sizeof(bitmap->map) * 8) { + return 0; /* stale, outside the window */ + } else if (bitmap->map & (((uint64_t)1) << shift)) { + return 0; /* record previously received */ + } + + memcpy(s->s3->rrec.seq_num, seq, 8); + return 1; +} + +static void dtls1_record_bitmap_update(SSL *s, DTLS1_BITMAP *bitmap) { + int cmp; + unsigned int shift; + const uint8_t *seq = s->s3->read_sequence; + + cmp = satsub64be(seq, bitmap->max_seq_num); + if (cmp > 0) { + shift = cmp; + if (shift < sizeof(bitmap->map) * 8) { + bitmap->map <<= shift, bitmap->map |= 1UL; + } else { + bitmap->map = 1UL; + } + memcpy(bitmap->max_seq_num, seq, 8); + } else { + shift = -cmp; + if (shift < sizeof(bitmap->map) * 8) { + bitmap->map |= ((uint64_t)1) << shift; + } + } +} + +int dtls1_dispatch_alert(SSL *s) { + int i, j; + void (*cb)(const SSL *ssl, int type, int val) = NULL; + uint8_t buf[DTLS1_AL_HEADER_LENGTH]; + uint8_t *ptr = &buf[0]; + + s->s3->alert_dispatch = 0; + + memset(buf, 0x00, sizeof(buf)); + *ptr++ = s->s3->send_alert[0]; + *ptr++ = s->s3->send_alert[1]; + + i = do_dtls1_write(s, SSL3_RT_ALERT, &buf[0], sizeof(buf)); + if (i <= 0) { + s->s3->alert_dispatch = 1; + } else { + if (s->s3->send_alert[0] == SSL3_AL_FATAL) { + (void)BIO_flush(s->wbio); + } + + if (s->msg_callback) { + s->msg_callback(1, s->version, SSL3_RT_ALERT, s->s3->send_alert, 2, s, + s->msg_callback_arg); + } + + if (s->info_callback != NULL) { + cb = s->info_callback; + } else if (s->ctx->info_callback != NULL) { + cb = s->ctx->info_callback; + } + + if (cb != NULL) { + j = (s->s3->send_alert[0] << 8) | s->s3->send_alert[1]; + cb(s, SSL_CB_WRITE_ALERT, j); + } + } + + return i; +} + +static DTLS1_BITMAP *dtls1_get_bitmap(SSL *s, SSL3_RECORD *rr, + unsigned int *is_next_epoch) { + *is_next_epoch = 0; + + /* In current epoch, accept HM, CCS, DATA, & ALERT */ + if (rr->epoch == s->d1->r_epoch) { + return &s->d1->bitmap; + } else if (rr->epoch == (unsigned long)(s->d1->r_epoch + 1) && + (rr->type == SSL3_RT_HANDSHAKE || rr->type == SSL3_RT_ALERT)) { + /* Only HM and ALERT messages can be from the next epoch */ + *is_next_epoch = 1; + return &s->d1->next_bitmap; + } + + return NULL; +} + +void dtls1_reset_seq_numbers(SSL *s, int rw) { + uint8_t *seq; + unsigned int seq_bytes = sizeof(s->s3->read_sequence); + + if (rw & SSL3_CC_READ) { + seq = s->s3->read_sequence; + s->d1->r_epoch++; + memcpy(&(s->d1->bitmap), &(s->d1->next_bitmap), sizeof(DTLS1_BITMAP)); + memset(&(s->d1->next_bitmap), 0x00, sizeof(DTLS1_BITMAP)); + } else { + seq = s->s3->write_sequence; + memcpy(s->d1->last_write_sequence, seq, sizeof(s->s3->write_sequence)); + s->d1->w_epoch++; + } + + memset(seq, 0x00, seq_bytes); +} diff --git a/src/ssl/d1_srtp.c b/src/ssl/d1_srtp.c new file mode 100644 index 0000000..b85ff9b --- /dev/null +++ b/src/ssl/d1_srtp.c @@ -0,0 +1,431 @@ +/* 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 <stdio.h> + +#include <openssl/bytestring.h> +#include <openssl/obj.h> +#include <openssl/err.h> + +#include "ssl_locl.h" +#include <openssl/srtp.h> + + +static const SRTP_PROTECTION_PROFILE srtp_known_profiles[] = { + { + "SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80, + }, + { + "SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32, + }, + {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 = srtp_known_profiles; + while (p->name) { + if (len == strlen(p->name) && !strncmp(p->name, profile_name, len)) { + *pptr = p; + return 1; + } + + p++; + } + + return 0; +} + +static int find_profile_by_num(unsigned profile_num, + const SRTP_PROTECTION_PROFILE **pptr) { + const SRTP_PROTECTION_PROFILE *p; + + p = srtp_known_profiles; + while (p->name) { + if (p->id == profile_num) { + *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; + + const char *col; + const char *ptr = profiles_string; + + profiles = sk_SRTP_PROTECTION_PROFILE_new_null(); + if (profiles == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_ctx_make_profiles, + SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES); + return 0; + } + + do { + const SRTP_PROTECTION_PROFILE *p; + + col = strchr(ptr, ':'); + if (find_profile_by_name(ptr, &p, col ? col - ptr : strlen(ptr))) { + sk_SRTP_PROTECTION_PROFILE_push(profiles, p); + } else { + OPENSSL_PUT_ERROR(SSL, ssl_ctx_make_profiles, + SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE); + return 0; + } + + if (col) { + ptr = col + 1; + } + } while (col); + + *out = profiles; + + return 1; +} + +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 *s, const char *profiles) { + return ssl_ctx_make_profiles(profiles, &s->srtp_profiles); +} + +STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(SSL *s) { + if (s == NULL) { + return NULL; + } + + if (s->srtp_profiles != NULL) { + return s->srtp_profiles; + } + + if (s->ctx != NULL && s->ctx->srtp_profiles != NULL) { + return s->ctx->srtp_profiles; + } + + return NULL; +} + +const SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *s) { + return s->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 *s, const char *profiles) { + /* This API inverts its return value. */ + return !SSL_set_srtp_profiles(s, profiles); +} + +/* Note: this function returns 0 length if there are no profiles specified */ +int ssl_add_clienthello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen) { + int ct = 0; + int i; + STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = 0; + const SRTP_PROTECTION_PROFILE *prof; + + clnt = SSL_get_srtp_profiles(s); + ct = sk_SRTP_PROTECTION_PROFILE_num(clnt); /* -1 if clnt == 0 */ + + if (p) { + if (ct == 0) { + OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_use_srtp_ext, + SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST); + return 0; + } + + if (2 + ct * 2 + 1 > maxlen) { + OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_use_srtp_ext, + SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG); + return 0; + } + + /* Add the length */ + s2n(ct * 2, p); + for (i = 0; i < ct; i++) { + prof = sk_SRTP_PROTECTION_PROFILE_value(clnt, i); + s2n(prof->id, p); + } + + /* Add an empty use_mki value */ + *p++ = 0; + } + + *len = 2 + ct * 2 + 1; + + return 1; +} + +int ssl_parse_clienthello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert) { + CBS profile_ids, srtp_mki; + const SRTP_PROTECTION_PROFILE *cprof, *sprof; + STACK_OF(SRTP_PROTECTION_PROFILE) *client_profiles = 0, *server_profiles; + size_t i, j; + int ret = 0; + + if (!CBS_get_u16_length_prefixed(cbs, &profile_ids) || + CBS_len(&profile_ids) < 2 || + !CBS_get_u8_length_prefixed(cbs, &srtp_mki) || + CBS_len(cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_use_srtp_ext, + SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + *out_alert = SSL_AD_DECODE_ERROR; + goto done; + } + + client_profiles = sk_SRTP_PROTECTION_PROFILE_new_null(); + if (client_profiles == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_use_srtp_ext, + ERR_R_MALLOC_FAILURE); + *out_alert = SSL_AD_INTERNAL_ERROR; + goto done; + } + + while (CBS_len(&profile_ids) > 0) { + uint16_t profile_id; + + if (!CBS_get_u16(&profile_ids, &profile_id)) { + *out_alert = SSL_AD_DECODE_ERROR; + goto done; + } + + if (find_profile_by_num(profile_id, &cprof)) { + sk_SRTP_PROTECTION_PROFILE_push(client_profiles, cprof); + } + } + + /* Discard the MKI value for now. */ + + server_profiles = SSL_get_srtp_profiles(s); + + /* Pick the server's most preferred profile. */ + for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(server_profiles); i++) { + sprof = sk_SRTP_PROTECTION_PROFILE_value(server_profiles, i); + + for (j = 0; j < sk_SRTP_PROTECTION_PROFILE_num(client_profiles); j++) { + cprof = sk_SRTP_PROTECTION_PROFILE_value(client_profiles, j); + + if (cprof->id == sprof->id) { + s->srtp_profile = sprof; + ret = 1; + goto done; + } + } + } + + ret = 1; + +done: + if (client_profiles) { + sk_SRTP_PROTECTION_PROFILE_free(client_profiles); + } + + return ret; +} + +int ssl_add_serverhello_use_srtp_ext(SSL *s, unsigned char *p, int *len, + int maxlen) { + if (p) { + if (maxlen < 5) { + OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_use_srtp_ext, + SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG); + return 0; + } + + if (s->srtp_profile == 0) { + OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_use_srtp_ext, + SSL_R_USE_SRTP_NOT_NEGOTIATED); + return 0; + } + + s2n(2, p); + s2n(s->srtp_profile->id, p); + *p++ = 0; + } + + *len = 5; + + return 1; +} + +int ssl_parse_serverhello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert) { + CBS profile_ids, srtp_mki; + uint16_t profile_id; + size_t i; + + STACK_OF(SRTP_PROTECTION_PROFILE) *client_profiles; + const SRTP_PROTECTION_PROFILE *prof; + + /* 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 */ + if (!CBS_get_u16_length_prefixed(cbs, &profile_ids) || + !CBS_get_u16(&profile_ids, &profile_id) || CBS_len(&profile_ids) != 0 || + !CBS_get_u8_length_prefixed(cbs, &srtp_mki) || CBS_len(cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_use_srtp_ext, + SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (CBS_len(&srtp_mki) != 0) { + /* Must be no MKI, since we never offer one. */ + OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_use_srtp_ext, + SSL_R_BAD_SRTP_MKI_VALUE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + client_profiles = SSL_get_srtp_profiles(s); + + /* Throw an error if the server gave us an unsolicited extension */ + if (client_profiles == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_use_srtp_ext, + SSL_R_NO_SRTP_PROFILES); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Check to see if the server gave us something we support + (and presumably offered). */ + for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(client_profiles); i++) { + prof = sk_SRTP_PROTECTION_PROFILE_value(client_profiles, i); + + if (prof->id == profile_id) { + s->srtp_profile = prof; + *out_alert = 0; + return 1; + } + } + + OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_use_srtp_ext, + SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; +} diff --git a/src/ssl/d1_srvr.c b/src/ssl/d1_srvr.c new file mode 100644 index 0000000..5bce98e --- /dev/null +++ b/src/ssl/d1_srvr.c @@ -0,0 +1,605 @@ +/* + * DTLS implementation written by Nagendra Modadugu + * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. + */ +/* ==================================================================== + * Copyright (c) 1999-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 (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 <assert.h> +#include <stdio.h> + +#include <openssl/bn.h> +#include <openssl/buf.h> +#include <openssl/dh.h> +#include <openssl/evp.h> +#include <openssl/md5.h> +#include <openssl/obj.h> +#include <openssl/rand.h> +#include <openssl/x509.h> + +#include "ssl_locl.h" + + +static int dtls1_send_hello_verify_request(SSL *s); + +int dtls1_accept(SSL *s) { + BUF_MEM *buf = NULL; + void (*cb)(const SSL *ssl, int type, int val) = NULL; + unsigned long alg_a; + int ret = -1; + int new_state, state, skip = 0; + + assert(s->handshake_func == dtls1_accept); + assert(s->server); + assert(SSL_IS_DTLS(s)); + + ERR_clear_error(); + ERR_clear_system_error(); + + if (s->info_callback != NULL) { + cb = s->info_callback; + } else if (s->ctx->info_callback != NULL) { + cb = s->ctx->info_callback; + } + + s->in_handshake++; + + if (s->cert == NULL) { + OPENSSL_PUT_ERROR(SSL, dtls1_accept, SSL_R_NO_CERTIFICATE_SET); + return -1; + } + + for (;;) { + state = s->state; + + switch (s->state) { + case SSL_ST_RENEGOTIATE: + s->renegotiate = 1; + /* s->state=SSL_ST_ACCEPT; */ + + case SSL_ST_ACCEPT: + case SSL_ST_BEFORE | SSL_ST_ACCEPT: + if (cb != NULL) { + cb(s, SSL_CB_HANDSHAKE_START, 1); + } + + if (s->init_buf == NULL) { + buf = BUF_MEM_new(); + if (buf == NULL || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { + ret = -1; + goto end; + } + s->init_buf = buf; + buf = NULL; + } + + if (!ssl3_setup_buffers(s)) { + ret = -1; + goto end; + } + + s->init_num = 0; + + if (s->state != SSL_ST_RENEGOTIATE) { + if (!ssl_init_wbio_buffer(s, 1)) { + ret = -1; + goto end; + } + + if (!ssl3_init_finished_mac(s)) { + OPENSSL_PUT_ERROR(SSL, dtls1_accept, ERR_R_INTERNAL_ERROR); + ret = -1; + goto end; + } + + s->state = SSL3_ST_SR_CLNT_HELLO_A; + s->ctx->stats.sess_accept++; + } else { + /* s->state == SSL_ST_RENEGOTIATE, * we will just send a + * HelloRequest */ + s->ctx->stats.sess_accept_renegotiate++; + s->state = SSL3_ST_SW_HELLO_REQ_A; + } + + break; + + case SSL3_ST_SW_HELLO_REQ_A: + case SSL3_ST_SW_HELLO_REQ_B: + s->shutdown = 0; + dtls1_clear_record_buffer(s); + dtls1_start_timer(s); + ret = ssl3_send_hello_request(s); + if (ret <= 0) { + goto end; + } + s->s3->tmp.next_state = SSL3_ST_SR_CLNT_HELLO_A; + s->state = SSL3_ST_SW_FLUSH; + s->init_num = 0; + + if (!ssl3_init_finished_mac(s)) { + OPENSSL_PUT_ERROR(SSL, dtls1_accept, ERR_R_INTERNAL_ERROR); + ret = -1; + goto end; + } + break; + + case SSL3_ST_SW_HELLO_REQ_C: + s->state = SSL_ST_OK; + break; + + case SSL3_ST_SR_CLNT_HELLO_A: + case SSL3_ST_SR_CLNT_HELLO_B: + case SSL3_ST_SR_CLNT_HELLO_C: + case SSL3_ST_SR_CLNT_HELLO_D: + s->shutdown = 0; + ret = ssl3_get_client_hello(s); + if (ret <= 0) { + goto end; + } + dtls1_stop_timer(s); + + if (ret == 1 && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE)) { + s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A; + } else { + s->state = SSL3_ST_SW_SRVR_HELLO_A; + } + + s->init_num = 0; + break; + + case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A: + case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B: + ret = dtls1_send_hello_verify_request(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SW_FLUSH; + s->s3->tmp.next_state = SSL3_ST_SR_CLNT_HELLO_A; + + /* HelloVerifyRequest resets Finished MAC */ + if (!ssl3_init_finished_mac(s)) { + OPENSSL_PUT_ERROR(SSL, dtls1_accept, ERR_R_INTERNAL_ERROR); + ret = -1; + goto end; + } + break; + + case SSL3_ST_SW_SRVR_HELLO_A: + case SSL3_ST_SW_SRVR_HELLO_B: + s->renegotiate = 2; + dtls1_start_timer(s); + ret = ssl3_send_server_hello(s); + if (ret <= 0) { + goto end; + } + + if (s->hit) { + if (s->tlsext_ticket_expected) { + s->state = SSL3_ST_SW_SESSION_TICKET_A; + } else { + s->state = SSL3_ST_SW_CHANGE_A; + } + } else { + s->state = SSL3_ST_SW_CERT_A; + } + s->init_num = 0; + break; + + case SSL3_ST_SW_CERT_A: + case SSL3_ST_SW_CERT_B: + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + dtls1_start_timer(s); + ret = ssl3_send_server_certificate(s); + if (ret <= 0) { + goto end; + } + if (s->s3->tmp.certificate_status_expected) { + s->state = SSL3_ST_SW_CERT_STATUS_A; + } else { + s->state = SSL3_ST_SW_KEY_EXCH_A; + } + } else { + skip = 1; + s->state = SSL3_ST_SW_KEY_EXCH_A; + } + s->init_num = 0; + break; + + case SSL3_ST_SW_KEY_EXCH_A: + case SSL3_ST_SW_KEY_EXCH_B: + alg_a = s->s3->tmp.new_cipher->algorithm_auth; + + /* Send a ServerKeyExchange message if: + * - The key exchange is ephemeral or anonymous + * Diffie-Hellman. + * - There is a PSK identity hint. + * + * TODO(davidben): This logic is currently duplicated + * in s3_srvr.c. Fix this. In the meantime, keep them + * in sync. */ + if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher) || + ((alg_a & SSL_aPSK) && s->psk_identity_hint)) { + dtls1_start_timer(s); + ret = ssl3_send_server_key_exchange(s); + if (ret <= 0) { + goto end; + } + } else { + skip = 1; + } + + s->state = SSL3_ST_SW_CERT_REQ_A; + s->init_num = 0; + break; + + case SSL3_ST_SW_CERT_REQ_A: + case SSL3_ST_SW_CERT_REQ_B: + if (/* don't request cert unless asked for it: */ + !(s->verify_mode & SSL_VERIFY_PEER) || + /* if SSL_VERIFY_CLIENT_ONCE is set, + * don't request cert during re-negotiation: */ + ((s->session->peer != NULL) && + (s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) || + /* never request cert in anonymous ciphersuites + * (see section "Certificate request" in SSL 3 drafts + * and in RFC 2246): */ + ((s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) && + /* ... except when the application insists on verification + * (against the specs, but s3_clnt.c accepts this for SSL 3) */ + !(s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) || + /* With normal PSK Certificates and + * Certificate Requests are omitted */ + (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) { + /* no cert request */ + skip = 1; + s->s3->tmp.cert_request = 0; + s->state = SSL3_ST_SW_SRVR_DONE_A; + } else { + s->s3->tmp.cert_request = 1; + dtls1_start_timer(s); + ret = ssl3_send_certificate_request(s); + if (ret <= 0) { + goto end; + } +#ifndef NETSCAPE_HANG_BUG + s->state = SSL3_ST_SW_SRVR_DONE_A; +#else + s->state = SSL3_ST_SW_FLUSH; + s->s3->tmp.next_state = SSL3_ST_SR_CERT_A; +#endif + s->init_num = 0; + } + break; + + case SSL3_ST_SW_SRVR_DONE_A: + case SSL3_ST_SW_SRVR_DONE_B: + dtls1_start_timer(s); + ret = ssl3_send_server_done(s); + if (ret <= 0) { + goto end; + } + s->s3->tmp.next_state = SSL3_ST_SR_CERT_A; + s->state = SSL3_ST_SW_FLUSH; + s->init_num = 0; + break; + + case SSL3_ST_SW_FLUSH: + s->rwstate = SSL_WRITING; + if (BIO_flush(s->wbio) <= 0) { + /* If the write error was fatal, stop trying */ + if (!BIO_should_retry(s->wbio)) { + s->rwstate = SSL_NOTHING; + s->state = s->s3->tmp.next_state; + } + + ret = -1; + goto end; + } + s->rwstate = SSL_NOTHING; + s->state = s->s3->tmp.next_state; + break; + + case SSL3_ST_SR_CERT_A: + case SSL3_ST_SR_CERT_B: + if (s->s3->tmp.cert_request) { + ret = ssl3_get_client_certificate(s); + if (ret <= 0) { + goto end; + } + } + s->init_num = 0; + s->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(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SR_CERT_VRFY_A; + s->init_num = 0; + break; + + case SSL3_ST_SR_CERT_VRFY_A: + case SSL3_ST_SR_CERT_VRFY_B: + ret = ssl3_get_cert_verify(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SR_FINISHED_A; + s->init_num = 0; + break; + + case SSL3_ST_SR_FINISHED_A: + case SSL3_ST_SR_FINISHED_B: + s->d1->change_cipher_spec_ok = 1; + ret = + ssl3_get_finished(s, SSL3_ST_SR_FINISHED_A, SSL3_ST_SR_FINISHED_B); + if (ret <= 0) { + goto end; + } + dtls1_stop_timer(s); + if (s->hit) { + s->state = SSL_ST_OK; + } else if (s->tlsext_ticket_expected) { + s->state = SSL3_ST_SW_SESSION_TICKET_A; + } else { + s->state = SSL3_ST_SW_CHANGE_A; + } + s->init_num = 0; + break; + + case SSL3_ST_SW_SESSION_TICKET_A: + case SSL3_ST_SW_SESSION_TICKET_B: + ret = ssl3_send_new_session_ticket(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SW_CHANGE_A; + s->init_num = 0; + break; + + case SSL3_ST_SW_CHANGE_A: + case SSL3_ST_SW_CHANGE_B: + s->session->cipher = s->s3->tmp.new_cipher; + if (!s->enc_method->setup_key_block(s)) { + ret = -1; + goto end; + } + + ret = dtls1_send_change_cipher_spec(s, SSL3_ST_SW_CHANGE_A, + SSL3_ST_SW_CHANGE_B); + + if (ret <= 0) { + goto end; + } + + s->state = SSL3_ST_SW_FINISHED_A; + s->init_num = 0; + + if (!s->enc_method->change_cipher_state( + s, SSL3_CHANGE_CIPHER_SERVER_WRITE)) { + ret = -1; + goto end; + } + + dtls1_reset_seq_numbers(s, SSL3_CC_WRITE); + break; + + case SSL3_ST_SW_FINISHED_A: + case SSL3_ST_SW_FINISHED_B: + ret = + ssl3_send_finished(s, SSL3_ST_SW_FINISHED_A, SSL3_ST_SW_FINISHED_B, + s->enc_method->server_finished_label, + s->enc_method->server_finished_label_len); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SW_FLUSH; + if (s->hit) { + s->s3->tmp.next_state = SSL3_ST_SR_FINISHED_A; + } else { + s->s3->tmp.next_state = SSL_ST_OK; + } + s->init_num = 0; + break; + + case SSL_ST_OK: + ssl3_cleanup_key_block(s); + + /* remove buffering on output */ + ssl_free_wbio_buffer(s); + + s->init_num = 0; + + if (s->renegotiate == 2) { + /* skipped if we just sent a HelloRequest */ + s->renegotiate = 0; + s->new_session = 0; + + ssl_update_cache(s, SSL_SESS_CACHE_SERVER); + + s->ctx->stats.sess_accept_good++; + + if (cb != NULL) { + cb(s, SSL_CB_HANDSHAKE_DONE, 1); + } + } + + ret = 1; + + /* done handshaking, next message is client hello */ + s->d1->handshake_read_seq = 0; + /* next message is server hello */ + s->d1->handshake_write_seq = 0; + s->d1->next_handshake_write_seq = 0; + goto end; + + default: + OPENSSL_PUT_ERROR(SSL, dtls1_accept, SSL_R_UNKNOWN_STATE); + ret = -1; + goto end; + } + + if (!s->s3->tmp.reuse_message && !skip) { + if (cb != NULL && s->state != state) { + new_state = s->state; + s->state = state; + cb(s, SSL_CB_ACCEPT_LOOP, 1); + s->state = new_state; + } + } + skip = 0; + } + +end: + s->in_handshake--; + if (buf != NULL) { + BUF_MEM_free(buf); + } + if (cb != NULL) { + cb(s, SSL_CB_ACCEPT_EXIT, ret); + } + return ret; +} + +int dtls1_send_hello_verify_request(SSL *s) { + uint8_t *msg, *p; + + if (s->state == DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A) { + msg = p = ssl_handshake_start(s); + /* Always use DTLS 1.0 version: see RFC 6347 */ + *(p++) = DTLS1_VERSION >> 8; + *(p++) = DTLS1_VERSION & 0xFF; + + /* Inform the callback how much space is in the + * cookie's buffer. */ + s->d1->cookie_len = sizeof(s->d1->cookie); + + if (s->ctx->app_gen_cookie_cb == NULL || + s->ctx->app_gen_cookie_cb(s, s->d1->cookie, &(s->d1->cookie_len)) == + 0) { + OPENSSL_PUT_ERROR(SSL, dtls1_send_hello_verify_request, + ERR_R_INTERNAL_ERROR); + return 0; + } + + *(p++) = (uint8_t)s->d1->cookie_len; + memcpy(p, s->d1->cookie, s->d1->cookie_len); + p += s->d1->cookie_len; + + ssl_set_handshake_header(s, DTLS1_MT_HELLO_VERIFY_REQUEST, p - msg); + s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B; + } + + /* s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B */ + return ssl_do_write(s); +} diff --git a/src/ssl/pqueue/CMakeLists.txt b/src/ssl/pqueue/CMakeLists.txt new file mode 100644 index 0000000..9f14020 --- /dev/null +++ b/src/ssl/pqueue/CMakeLists.txt @@ -0,0 +1,17 @@ +include_directories(../../include) + +add_library( + pqueue + + OBJECT + + pqueue.c +) + +add_executable( + pqueue_test + + pqueue_test.c +) + +target_link_libraries(pqueue_test ssl crypto) diff --git a/src/ssl/pqueue/pqueue.c b/src/ssl/pqueue/pqueue.c new file mode 100644 index 0000000..ecaa139 --- /dev/null +++ b/src/ssl/pqueue/pqueue.c @@ -0,0 +1,194 @@ +/* + * 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/pqueue.h> + +#include <string.h> + +#include <openssl/mem.h> + + +typedef struct _pqueue { + pitem *items; + unsigned count; +} pqueue_s; + + +pitem *pitem_new(uint8_t prio64be[8], void *data) { + pitem *item = (pitem *)OPENSSL_malloc(sizeof(pitem)); + if (item == NULL) { + return NULL; + } + + memcpy(item->priority, prio64be, sizeof(item->priority)); + + item->data = data; + item->next = NULL; + + return item; +} + +void pitem_free(pitem *item) { + if (item == NULL) { + return; + } + + OPENSSL_free(item); +} + +pqueue pqueue_new(void) { + pqueue_s *pq = (pqueue_s *)OPENSSL_malloc(sizeof(pqueue_s)); + if (pq == NULL) { + return NULL; + } + + memset(pq, 0, sizeof(pqueue_s)); + return pq; +} + +void pqueue_free(pqueue_s *pq) { + if (pq == NULL) { + return; + } + + OPENSSL_free(pq); +} + +pitem *pqueue_peek(pqueue_s *pq) { return pq->items; } + +pitem *pqueue_find(pqueue_s *pq, uint8_t *prio64be) { + pitem *curr; + + for (curr = pq->items; curr; curr = curr->next) { + if (memcmp(curr->priority, prio64be, sizeof(curr->priority)) == 0) { + return curr; + } + } + + return NULL; +} + +size_t pqueue_size(pqueue_s *pq) { + pitem *item = pq->items; + size_t count = 0; + + while (item != NULL) { + count++; + item = item->next; + } + return count; +} + +piterator pqueue_iterator(pqueue_s *pq) { return pq->items; } + +pitem *pqueue_next(piterator *item) { + pitem *ret; + + if (item == NULL || *item == NULL) { + return NULL; + } + + ret = *item; + *item = (*item)->next; + + return ret; +} + +pitem *pqueue_insert(pqueue_s *pq, pitem *item) { + pitem *curr, *next; + + if (pq->items == NULL) { + pq->items = item; + return item; + } + + for (curr = NULL, next = pq->items; next != NULL; + curr = next, next = next->next) { + /* we can compare 64-bit value in big-endian encoding with memcmp. */ + int cmp = memcmp(next->priority, item->priority, sizeof(item->priority)); + if (cmp > 0) { + /* next > item */ + item->next = next; + + if (curr == NULL) { + pq->items = item; + } else { + curr->next = item; + } + + return item; + } else if (cmp == 0) { + /* duplicates not allowed */ + return NULL; + } + } + + item->next = NULL; + curr->next = item; + + return item; +} + + +pitem *pqueue_pop(pqueue_s *pq) { + pitem *item = pq->items; + + if (pq->items != NULL) { + pq->items = pq->items->next; + } + + return item; +} diff --git a/src/ssl/pqueue/pqueue_test.c b/src/ssl/pqueue/pqueue_test.c new file mode 100644 index 0000000..c4b4b9d --- /dev/null +++ b/src/ssl/pqueue/pqueue_test.c @@ -0,0 +1,117 @@ +/* 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 <openssl/pqueue.h> +#include <openssl/ssl.h> + + +static int trivial(void) { + pqueue q = pqueue_new(); + if (q == NULL) { + return 0; + } + int32_t data = 0xdeadbeef; + uint8_t priority[8] = {0}; + pitem *item = pitem_new(priority, &data); + if (item == NULL || + pqueue_insert(q, item) != item || + pqueue_size(q) != 1 || + pqueue_peek(q) != item || + pqueue_pop(q) != item || + pqueue_size(q) != 0 || + pqueue_pop(q) != NULL) { + return 0; + } + pitem_free(item); + pqueue_free(q); + return 1; +} + +#define NUM_ITEMS 10 + +static int fixed_random(void) { + /* Random order of 10 elements, chosen by + * random.choice(list(itertools.permutations(range(10)))) */ + int ordering[NUM_ITEMS] = {9, 6, 3, 4, 0, 2, 7, 1, 8, 5}; + int i; + pqueue q = pqueue_new(); + uint8_t priority[8] = {0}; + piterator iter; + pitem *curr, *item; + + if (q == NULL) { + return 0; + } + + /* Insert the elements */ + for (i = 0; i < NUM_ITEMS; i++) { + priority[7] = ordering[i]; + item = pitem_new(priority, &ordering[i]); + if (pqueue_insert(q, item) != item) { + return 0; + } + } + + /* Insert the elements again. This inserts duplicates and should + * fail. */ + for (i = 0; i < NUM_ITEMS; i++) { + priority[7] = ordering[i]; + item = pitem_new(priority, &ordering[i]); + if (pqueue_insert(q, item) != NULL) { + return 0; + } + pitem_free(item); + } + + if (pqueue_size(q) != NUM_ITEMS) { + return 0; + } + + /* Iterate over the elements. */ + iter = pqueue_iterator(q); + curr = pqueue_next(&iter); + if (curr == NULL) { + return 0; + } + while (1) { + pitem *next = pqueue_next(&iter); + int *curr_data, *next_data; + + if (next == NULL) { + break; + } + curr_data = (int*)curr->data; + next_data = (int*)next->data; + if (*curr_data >= *next_data) { + return 0; + } + curr = next; + } + pqueue_free(q); + return 1; +} + +int main(void) { + SSL_library_init(); + + if (!trivial() || !fixed_random()) { + return 1; + } + + printf("PASS\n"); + return 0; +} diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c new file mode 100644 index 0000000..a34d221 --- /dev/null +++ b/src/ssl/s3_both.c @@ -0,0 +1,699 @@ +/* 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 <assert.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> + +#include <openssl/buf.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/md5.h> +#include <openssl/obj.h> +#include <openssl/rand.h> +#include <openssl/sha.h> +#include <openssl/x509.h> + +#include "ssl_locl.h" + + +/* ssl3_do_write sends |s->init_buf| in records of type 'type' + * (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC). It returns -1 on error, 1 + * on success or zero if the transmission is still incomplete. */ +int ssl3_do_write(SSL *s, int type) { + int n; + + n = ssl3_write_bytes(s, type, &s->init_buf->data[s->init_off], s->init_num); + if (n < 0) { + return -1; + } + + if (n == s->init_num) { + if (s->msg_callback) { + s->msg_callback(1, s->version, type, s->init_buf->data, + (size_t)(s->init_off + s->init_num), s, + s->msg_callback_arg); + } + return 1; + } + + s->init_off += n; + s->init_num -= n; + return 0; +} + +int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen) { + uint8_t *p; + int n; + + if (s->state == a) { + p = ssl_handshake_start(s); + + n = s->enc_method->final_finish_mac(s, sender, slen, s->s3->tmp.finish_md); + if (n == 0) { + return 0; + } + s->s3->tmp.finish_md_len = n; + memcpy(p, s->s3->tmp.finish_md, n); + + /* Log the master secret, if logging is enabled. */ + if (!ssl_ctx_log_master_secret(s->ctx, s->s3->client_random, + SSL3_RANDOM_SIZE, s->session->master_key, + s->session->master_key_length)) { + return 0; + } + + /* Copy the finished so we can use it for + * renegotiation checks */ + if (s->server) { + assert(n <= EVP_MAX_MD_SIZE); + memcpy(s->s3->previous_server_finished, s->s3->tmp.finish_md, n); + s->s3->previous_server_finished_len = n; + } else { + assert(n <= EVP_MAX_MD_SIZE); + memcpy(s->s3->previous_client_finished, s->s3->tmp.finish_md, n); + s->s3->previous_client_finished_len = n; + } + + ssl_set_handshake_header(s, SSL3_MT_FINISHED, n); + s->state = b; + } + + /* SSL3_ST_SEND_xxxxxx_HELLO_B */ + return ssl_do_write(s); +} + +/* ssl3_take_mac calculates the Finished MAC for the handshakes messages seen to + * far. */ +static void ssl3_take_mac(SSL *s) { + const char *sender; + int slen; + + /* If no new cipher setup then return immediately: other functions will set + * the appropriate error. */ + if (s->s3->tmp.new_cipher == NULL) { + return; + } + + if (s->state & SSL_ST_CONNECT) { + sender = s->enc_method->server_finished_label; + slen = s->enc_method->server_finished_label_len; + } else { + sender = s->enc_method->client_finished_label; + slen = s->enc_method->client_finished_label_len; + } + + s->s3->tmp.peer_finish_md_len = s->enc_method->final_finish_mac( + s, sender, slen, s->s3->tmp.peer_finish_md); +} + +int ssl3_get_finished(SSL *s, int a, int b) { + int al, finished_len, ok; + long message_len; + uint8_t *p; + + message_len = + s->method->ssl_get_message(s, a, b, SSL3_MT_FINISHED, EVP_MAX_MD_SIZE, + SSL_GET_MESSAGE_DONT_HASH_MESSAGE, &ok); + + if (!ok) { + return message_len; + } + + /* Snapshot the finished hash before incorporating the new message. */ + ssl3_take_mac(s); + ssl3_hash_current_message(s); + + /* If this occurs, we have missed a message. + * TODO(davidben): Is this check now redundant with SSL3_FLAGS_EXPECT_CCS? */ + if (!s->s3->change_cipher_spec) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_finished, SSL_R_GOT_A_FIN_BEFORE_A_CCS); + goto f_err; + } + s->s3->change_cipher_spec = 0; + + p = s->init_msg; + finished_len = s->s3->tmp.peer_finish_md_len; + + if (finished_len != message_len) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_finished, SSL_R_BAD_DIGEST_LENGTH); + goto f_err; + } + + if (CRYPTO_memcmp(p, s->s3->tmp.peer_finish_md, finished_len) != 0) { + al = SSL_AD_DECRYPT_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_finished, SSL_R_DIGEST_CHECK_FAILED); + goto f_err; + } + + /* Copy the finished so we can use it for renegotiation checks */ + if (s->server) { + assert(finished_len <= EVP_MAX_MD_SIZE); + memcpy(s->s3->previous_client_finished, s->s3->tmp.peer_finish_md, finished_len); + s->s3->previous_client_finished_len = finished_len; + } else { + assert(finished_len <= EVP_MAX_MD_SIZE); + memcpy(s->s3->previous_server_finished, s->s3->tmp.peer_finish_md, finished_len); + s->s3->previous_server_finished_len = finished_len; + } + + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + return 0; +} + +/* for these 2 messages, we need to + * ssl->enc_read_ctx re-init + * ssl->s3->read_sequence zero + * ssl->s3->read_mac_secret re-init + * ssl->session->read_sym_enc assign + * ssl->session->read_compression assign + * ssl->session->read_hash assign */ +int ssl3_send_change_cipher_spec(SSL *s, int a, int b) { + if (s->state == a) { + *((uint8_t *)s->init_buf->data) = SSL3_MT_CCS; + s->init_num = 1; + s->init_off = 0; + + s->state = b; + } + + /* SSL3_ST_CW_CHANGE_B */ + return ssl3_do_write(s, SSL3_RT_CHANGE_CIPHER_SPEC); +} + +unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk) { + uint8_t *p; + unsigned long l = 3 + SSL_HM_HEADER_LENGTH(s); + + if (!ssl_add_cert_chain(s, cpk, &l)) { + return 0; + } + + l -= 3 + SSL_HM_HEADER_LENGTH(s); + p = ssl_handshake_start(s); + l2n3(l, p); + l += 3; + ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE, l); + return l + SSL_HM_HEADER_LENGTH(s); +} + +/* Obtain handshake message of message type |msg_type| (any if |msg_type| == -1), + * maximum acceptable body length |max|. The first four bytes (msg_type and + * length) are read in state |header_state|, the body is read in state |body_state|. */ +long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type, + long max, int hash_message, int *ok) { + uint8_t *p; + unsigned long l; + long n; + int al; + + if (s->s3->tmp.reuse_message) { + /* A SSL_GET_MESSAGE_DONT_HASH_MESSAGE call cannot be combined with + * reuse_message; the SSL_GET_MESSAGE_DONT_HASH_MESSAGE would have to have + * been applied to the previous call. */ + assert(hash_message != SSL_GET_MESSAGE_DONT_HASH_MESSAGE); + s->s3->tmp.reuse_message = 0; + if (msg_type >= 0 && s->s3->tmp.message_type != msg_type) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_message, SSL_R_UNEXPECTED_MESSAGE); + goto f_err; + } + *ok = 1; + s->state = body_state; + s->init_msg = (uint8_t *)s->init_buf->data + 4; + s->init_num = (int)s->s3->tmp.message_size; + return s->init_num; + } + + p = (uint8_t *)s->init_buf->data; + + if (s->state == header_state) { + assert(s->init_num < 4); + + for (;;) { + while (s->init_num < 4) { + int bytes_read = s->method->ssl_read_bytes( + s, SSL3_RT_HANDSHAKE, &p[s->init_num], 4 - s->init_num, 0); + if (bytes_read <= 0) { + s->rwstate = SSL_READING; + *ok = 0; + return bytes_read; + } + s->init_num += bytes_read; + } + + static const uint8_t kHelloRequest[4] = {SSL3_MT_HELLO_REQUEST, 0, 0, 0}; + if (s->server || memcmp(p, kHelloRequest, sizeof(kHelloRequest)) != 0) { + break; + } + + /* The server may always send 'Hello Request' messages -- we are doing + * a handshake anyway now, so ignore them if their format is correct. + * Does not count for 'Finished' MAC. */ + s->init_num = 0; + + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, p, 4, s, + s->msg_callback_arg); + } + } + + /* s->init_num == 4 */ + + if (msg_type >= 0 && *p != msg_type) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_message, SSL_R_UNEXPECTED_MESSAGE); + goto f_err; + } + s->s3->tmp.message_type = *(p++); + + n2l3(p, l); + if (l > (unsigned long)max) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_message, SSL_R_EXCESSIVE_MESSAGE_SIZE); + goto f_err; + } + + if (l && !BUF_MEM_grow_clean(s->init_buf, l + 4)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_message, ERR_R_BUF_LIB); + goto err; + } + s->s3->tmp.message_size = l; + s->state = body_state; + + s->init_msg = (uint8_t *)s->init_buf->data + 4; + s->init_num = 0; + } + + /* next state (body_state) */ + p = s->init_msg; + n = s->s3->tmp.message_size - s->init_num; + while (n > 0) { + int bytes_read = + s->method->ssl_read_bytes(s, SSL3_RT_HANDSHAKE, &p[s->init_num], n, 0); + if (bytes_read <= 0) { + s->rwstate = SSL_READING; + *ok = 0; + return bytes_read; + } + s->init_num += bytes_read; + n -= bytes_read; + } + + /* Feed this message into MAC computation. */ + if (hash_message != SSL_GET_MESSAGE_DONT_HASH_MESSAGE) { + ssl3_hash_current_message(s); + } + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, s->init_buf->data, + (size_t)s->init_num + 4, s, s->msg_callback_arg); + } + *ok = 1; + return s->init_num; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + +err: + *ok = 0; + return -1; +} + +void ssl3_hash_current_message(SSL *s) { + /* The handshake header (different size between DTLS and TLS) is included in + * the hash. */ + size_t header_len = s->init_msg - (uint8_t *)s->init_buf->data; + ssl3_finish_mac(s, (uint8_t *)s->init_buf->data, s->init_num + header_len); +} + +/* ssl3_cert_verify_hash is documented as needing EVP_MAX_MD_SIZE because that + * is sufficient pre-TLS1.2 as well. */ +OPENSSL_COMPILE_ASSERT(EVP_MAX_MD_SIZE > MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH, + combined_tls_hash_fits_in_max); + +int ssl3_cert_verify_hash(SSL *s, uint8_t *out, size_t *out_len, + const EVP_MD **out_md, EVP_PKEY *pkey) { + /* For TLS v1.2 send signature algorithm and signature using + * agreed digest and cached handshake records. Otherwise, use + * SHA1 or MD5 + SHA1 depending on key type. */ + if (SSL_USE_SIGALGS(s)) { + const uint8_t *hdata; + size_t hdatalen; + EVP_MD_CTX mctx; + unsigned len; + + if (!BIO_mem_contents(s->s3->handshake_buffer, &hdata, &hdatalen)) { + OPENSSL_PUT_ERROR(SSL, ssl3_cert_verify_hash, ERR_R_INTERNAL_ERROR); + return 0; + } + EVP_MD_CTX_init(&mctx); + if (!EVP_DigestInit_ex(&mctx, *out_md, NULL) || + !EVP_DigestUpdate(&mctx, hdata, hdatalen) || + !EVP_DigestFinal(&mctx, out, &len)) { + OPENSSL_PUT_ERROR(SSL, ssl3_cert_verify_hash, ERR_R_EVP_LIB); + EVP_MD_CTX_cleanup(&mctx); + return 0; + } + *out_len = len; + } else if (pkey->type == EVP_PKEY_RSA) { + if (s->enc_method->cert_verify_mac(s, NID_md5, out) == 0 || + s->enc_method->cert_verify_mac(s, NID_sha1, out + MD5_DIGEST_LENGTH) == + 0) { + return 0; + } + *out_len = MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH; + *out_md = EVP_md5_sha1(); + } else if (pkey->type == EVP_PKEY_EC) { + if (s->enc_method->cert_verify_mac(s, NID_sha1, out) == 0) { + return 0; + } + *out_len = SHA_DIGEST_LENGTH; + *out_md = EVP_sha1(); + } else { + OPENSSL_PUT_ERROR(SSL, ssl3_cert_verify_hash, ERR_R_INTERNAL_ERROR); + return 0; + } + + return 1; +} + +int ssl_cert_type(EVP_PKEY *pkey) { + switch (pkey->type) { + case EVP_PKEY_RSA: + return SSL_PKEY_RSA_ENC; + case EVP_PKEY_EC: + return SSL_PKEY_ECC; + default: + return -1; + } +} + +int ssl_verify_alarm_type(long type) { + int al; + + 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: + al = SSL_AD_UNKNOWN_CA; + break; + + 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: + al = SSL_AD_BAD_CERTIFICATE; + break; + + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509_V_ERR_CRL_SIGNATURE_FAILURE: + al = SSL_AD_DECRYPT_ERROR; + break; + + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_HAS_EXPIRED: + al = SSL_AD_CERTIFICATE_EXPIRED; + break; + + case X509_V_ERR_CERT_REVOKED: + al = SSL_AD_CERTIFICATE_REVOKED; + break; + + case X509_V_ERR_OUT_OF_MEM: + al = SSL_AD_INTERNAL_ERROR; + break; + + 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: + al = SSL_AD_UNKNOWN_CA; + break; + + case X509_V_ERR_APPLICATION_VERIFICATION: + al = SSL_AD_HANDSHAKE_FAILURE; + break; + + case X509_V_ERR_INVALID_PURPOSE: + al = SSL_AD_UNSUPPORTED_CERTIFICATE; + break; + + default: + al = SSL_AD_CERTIFICATE_UNKNOWN; + break; + } + + return al; +} + +int ssl3_setup_read_buffer(SSL *s) { + uint8_t *p; + size_t len, align = 0, headerlen; + + if (SSL_IS_DTLS(s)) { + headerlen = DTLS1_RT_HEADER_LENGTH; + } else { + headerlen = SSL3_RT_HEADER_LENGTH; + } + +#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 + align = (-SSL3_RT_HEADER_LENGTH) & (SSL3_ALIGN_PAYLOAD - 1); +#endif + + if (s->s3->rbuf.buf == NULL) { + len = SSL3_RT_MAX_PLAIN_LENGTH + SSL3_RT_MAX_ENCRYPTED_OVERHEAD + + headerlen + align; + if (s->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) { + s->s3->init_extra = 1; + len += SSL3_RT_MAX_EXTRA; + } + p = OPENSSL_malloc(len); + if (p == NULL) { + goto err; + } + s->s3->rbuf.buf = p; + s->s3->rbuf.len = len; + } + + s->packet = &s->s3->rbuf.buf[0]; + return 1; + +err: + OPENSSL_PUT_ERROR(SSL, ssl3_setup_read_buffer, ERR_R_MALLOC_FAILURE); + return 0; +} + +int ssl3_setup_write_buffer(SSL *s) { + uint8_t *p; + size_t len, align = 0, headerlen; + + if (SSL_IS_DTLS(s)) { + headerlen = DTLS1_RT_HEADER_LENGTH + 1; + } else { + headerlen = SSL3_RT_HEADER_LENGTH; + } + +#if defined(SSL3_ALIGN_PAYLOAD) && SSL3_ALIGN_PAYLOAD != 0 + align = (-SSL3_RT_HEADER_LENGTH) & (SSL3_ALIGN_PAYLOAD - 1); +#endif + + if (s->s3->wbuf.buf == NULL) { + len = s->max_send_fragment + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD + + headerlen + align; + /* Account for 1/n-1 record splitting. */ + if (s->mode & SSL_MODE_CBC_RECORD_SPLITTING) { + len += headerlen + align + 1 + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD; + } + + p = OPENSSL_malloc(len); + if (p == NULL) { + goto err; + } + s->s3->wbuf.buf = p; + s->s3->wbuf.len = len; + } + + return 1; + +err: + OPENSSL_PUT_ERROR(SSL, ssl3_setup_write_buffer, ERR_R_MALLOC_FAILURE); + return 0; +} + + +int ssl3_setup_buffers(SSL *s) { + if (!ssl3_setup_read_buffer(s) || + !ssl3_setup_write_buffer(s)) { + return 0; + } + return 1; +} + +int ssl3_release_write_buffer(SSL *s) { + if (s->s3->wbuf.buf != NULL) { + OPENSSL_free(s->s3->wbuf.buf); + s->s3->wbuf.buf = NULL; + } + return 1; +} + +int ssl3_release_read_buffer(SSL *s) { + if (s->s3->rbuf.buf != NULL) { + OPENSSL_free(s->s3->rbuf.buf); + s->s3->rbuf.buf = NULL; + } + return 1; +} + +/* ssl_fill_hello_random fills a client_random or server_random field of length + * |len|. Returns 0 on failure or 1 on success. */ +int ssl_fill_hello_random(SSL *s, int server, uint8_t *result, size_t len) { + int send_time = 0; + + if (server) { + send_time = (s->mode & SSL_MODE_SEND_SERVERHELLO_TIME) != 0; + } else { + send_time = (s->mode & SSL_MODE_SEND_CLIENTHELLO_TIME) != 0; + } + + if (send_time) { + const uint32_t current_time = time(NULL); + uint8_t *p = result; + + if (len < 4) { + return 0; + } + p[0] = current_time >> 24; + p[1] = current_time >> 16; + p[2] = current_time >> 8; + p[3] = current_time; + return RAND_bytes(p + 4, len - 4); + } else { + return RAND_bytes(result, len); + } +} diff --git a/src/ssl/s3_clnt.c b/src/ssl/s3_clnt.c new file mode 100644 index 0000000..231cc65 --- /dev/null +++ b/src/ssl/s3_clnt.c @@ -0,0 +1,2417 @@ +/* ssl/s3_clnt.c */ +/* 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 <assert.h> +#include <stdio.h> + +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/rand.h> +#include <openssl/obj.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/md5.h> +#include <openssl/dh.h> +#include <openssl/bn.h> +#include <openssl/engine.h> +#include <openssl/x509.h> + +#include "ssl_locl.h" +#include "../crypto/dh/internal.h" + + +int ssl3_connect(SSL *s) { + BUF_MEM *buf = NULL; + void (*cb)(const SSL *ssl, int type, int val) = NULL; + int ret = -1; + int new_state, state, skip = 0; + + assert(s->handshake_func == ssl3_connect); + assert(!s->server); + assert(!SSL_IS_DTLS(s)); + + ERR_clear_error(); + ERR_clear_system_error(); + + if (s->info_callback != NULL) { + cb = s->info_callback; + } else if (s->ctx->info_callback != NULL) { + cb = s->ctx->info_callback; + } + + s->in_handshake++; + + for (;;) { + state = s->state; + + switch (s->state) { + case SSL_ST_RENEGOTIATE: + s->renegotiate = 1; + s->state = SSL_ST_CONNECT; + s->ctx->stats.sess_connect_renegotiate++; + /* fallthrough */ + case SSL_ST_CONNECT: + case SSL_ST_BEFORE | SSL_ST_CONNECT: + if (cb != NULL) + cb(s, SSL_CB_HANDSHAKE_START, 1); + + if (s->init_buf == NULL) { + buf = BUF_MEM_new(); + if (buf == NULL || + !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { + ret = -1; + goto end; + } + + s->init_buf = buf; + buf = NULL; + } + + if (!ssl3_setup_buffers(s) || + !ssl_init_wbio_buffer(s, 0)) { + ret = -1; + goto end; + } + + /* don't push the buffering BIO quite yet */ + + if (!ssl3_init_finished_mac(s)) { + OPENSSL_PUT_ERROR(SSL, ssl3_connect, ERR_R_INTERNAL_ERROR); + ret = -1; + goto end; + } + + s->state = SSL3_ST_CW_CLNT_HELLO_A; + s->ctx->stats.sess_connect++; + s->init_num = 0; + break; + + case SSL3_ST_CW_CLNT_HELLO_A: + case SSL3_ST_CW_CLNT_HELLO_B: + s->shutdown = 0; + ret = ssl3_send_client_hello(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_SRVR_HELLO_A; + s->init_num = 0; + + /* turn on buffering for the next lot of output */ + if (s->bbio != s->wbio) { + s->wbio = BIO_push(s->bbio, s->wbio); + } + + break; + + case SSL3_ST_CR_SRVR_HELLO_A: + case SSL3_ST_CR_SRVR_HELLO_B: + ret = ssl3_get_server_hello(s); + if (ret <= 0) { + goto end; + } + + if (s->hit) { + s->state = SSL3_ST_CR_CHANGE; + if (s->tlsext_ticket_expected) { + /* receive renewed session ticket */ + s->state = SSL3_ST_CR_SESSION_TICKET_A; + } + } else { + s->state = SSL3_ST_CR_CERT_A; + } + s->init_num = 0; + break; + + case SSL3_ST_CR_CERT_A: + case SSL3_ST_CR_CERT_B: + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + ret = ssl3_get_server_certificate(s); + if (ret <= 0) { + goto end; + } + if (s->s3->tmp.certificate_status_expected) { + s->state = SSL3_ST_CR_CERT_STATUS_A; + } else { + s->state = SSL3_ST_CR_KEY_EXCH_A; + } + } else { + skip = 1; + s->state = SSL3_ST_CR_KEY_EXCH_A; + } + s->init_num = 0; + break; + + case SSL3_ST_CR_KEY_EXCH_A: + case SSL3_ST_CR_KEY_EXCH_B: + ret = ssl3_get_server_key_exchange(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_CERT_REQ_A; + s->init_num = 0; + + /* at this point we check that we have the + * required stuff from the server */ + if (!ssl3_check_cert_and_algorithm(s)) { + ret = -1; + goto end; + } + break; + + case SSL3_ST_CR_CERT_REQ_A: + case SSL3_ST_CR_CERT_REQ_B: + ret = ssl3_get_certificate_request(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_SRVR_DONE_A; + s->init_num = 0; + break; + + case SSL3_ST_CR_SRVR_DONE_A: + case SSL3_ST_CR_SRVR_DONE_B: + ret = ssl3_get_server_done(s); + if (ret <= 0) { + goto end; + } + if (s->s3->tmp.cert_req) { + s->state = SSL3_ST_CW_CERT_A; + } else { + s->state = SSL3_ST_CW_KEY_EXCH_A; + } + s->init_num = 0; + + break; + + case SSL3_ST_CW_CERT_A: + case SSL3_ST_CW_CERT_B: + case SSL3_ST_CW_CERT_C: + case SSL3_ST_CW_CERT_D: + ret = ssl3_send_client_certificate(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CW_KEY_EXCH_A; + s->init_num = 0; + break; + + case SSL3_ST_CW_KEY_EXCH_A: + case SSL3_ST_CW_KEY_EXCH_B: + ret = ssl3_send_client_key_exchange(s); + if (ret <= 0) { + goto end; + } + /* For TLS, cert_req is set to 2, so a cert chain + * of nothing is sent, but no verify packet is sent */ + if (s->s3->tmp.cert_req == 1) { + s->state = SSL3_ST_CW_CERT_VRFY_A; + } else { + s->state = SSL3_ST_CW_CHANGE_A; + s->s3->change_cipher_spec = 0; + } + + s->init_num = 0; + break; + + case SSL3_ST_CW_CERT_VRFY_A: + case SSL3_ST_CW_CERT_VRFY_B: + ret = ssl3_send_cert_verify(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CW_CHANGE_A; + s->init_num = 0; + s->s3->change_cipher_spec = 0; + break; + + case SSL3_ST_CW_CHANGE_A: + case SSL3_ST_CW_CHANGE_B: + ret = ssl3_send_change_cipher_spec(s, SSL3_ST_CW_CHANGE_A, + SSL3_ST_CW_CHANGE_B); + if (ret <= 0) { + goto end; + } + + s->state = SSL3_ST_CW_FINISHED_A; + if (s->s3->tlsext_channel_id_valid) { + s->state = SSL3_ST_CW_CHANNEL_ID_A; + } + if (s->s3->next_proto_neg_seen) { + s->state = SSL3_ST_CW_NEXT_PROTO_A; + } + s->init_num = 0; + + s->session->cipher = s->s3->tmp.new_cipher; + if (!s->enc_method->setup_key_block(s)) { + ret = -1; + goto end; + } + + if (!s->enc_method->change_cipher_state( + s, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) { + ret = -1; + goto end; + } + + break; + + case SSL3_ST_CW_NEXT_PROTO_A: + case SSL3_ST_CW_NEXT_PROTO_B: + ret = ssl3_send_next_proto(s); + if (ret <= 0) { + goto end; + } + + if (s->s3->tlsext_channel_id_valid) { + s->state = SSL3_ST_CW_CHANNEL_ID_A; + } else { + s->state = SSL3_ST_CW_FINISHED_A; + } + break; + + case SSL3_ST_CW_CHANNEL_ID_A: + case SSL3_ST_CW_CHANNEL_ID_B: + ret = ssl3_send_channel_id(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CW_FINISHED_A; + break; + + case SSL3_ST_CW_FINISHED_A: + case SSL3_ST_CW_FINISHED_B: + ret = + ssl3_send_finished(s, SSL3_ST_CW_FINISHED_A, SSL3_ST_CW_FINISHED_B, + s->enc_method->client_finished_label, + s->enc_method->client_finished_label_len); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CW_FLUSH; + + if (s->hit) { + s->s3->tmp.next_state = SSL_ST_OK; + } 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. */ + if (s->s3->tlsext_channel_id_new) { + ret = tls1_record_handshake_hashes_for_channel_id(s); + if (ret <= 0) + goto end; + } + if ((SSL_get_mode(s) & SSL_MODE_HANDSHAKE_CUTTHROUGH) && + ssl3_can_cutthrough(s) && + /* no cutthrough on renegotiation (would complicate the state + * machine) */ + s->s3->previous_server_finished_len == 0) { + s->s3->tmp.next_state = SSL3_ST_CUTTHROUGH_COMPLETE; + } else { + /* Allow NewSessionTicket if ticket expected */ + if (s->tlsext_ticket_expected) { + s->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A; + } else { + s->s3->tmp.next_state = SSL3_ST_CR_CHANGE; + } + } + } + s->init_num = 0; + break; + + case SSL3_ST_CR_SESSION_TICKET_A: + case SSL3_ST_CR_SESSION_TICKET_B: + ret = ssl3_get_new_session_ticket(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_CHANGE; + s->init_num = 0; + break; + + case SSL3_ST_CR_CERT_STATUS_A: + case SSL3_ST_CR_CERT_STATUS_B: + ret = ssl3_get_cert_status(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_CR_KEY_EXCH_A; + s->init_num = 0; + break; + + case SSL3_ST_CR_CHANGE: + /* At this point, the next message must be entirely behind a + * ChangeCipherSpec. */ + if (!ssl3_expect_change_cipher_spec(s)) { + ret = -1; + goto end; + } + s->state = SSL3_ST_CR_FINISHED_A; + break; + + case SSL3_ST_CR_FINISHED_A: + case SSL3_ST_CR_FINISHED_B: + ret = + ssl3_get_finished(s, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B); + if (ret <= 0) { + goto end; + } + + if (s->hit) { + s->state = SSL3_ST_CW_CHANGE_A; + } else { + s->state = SSL_ST_OK; + } + s->init_num = 0; + break; + + case SSL3_ST_CW_FLUSH: + s->rwstate = SSL_WRITING; + if (BIO_flush(s->wbio) <= 0) { + ret = -1; + goto end; + } + s->rwstate = SSL_NOTHING; + s->state = s->s3->tmp.next_state; + break; + + case SSL3_ST_CUTTHROUGH_COMPLETE: + /* Allow NewSessionTicket if ticket expected */ + if (s->tlsext_ticket_expected) { + s->state = SSL3_ST_CR_SESSION_TICKET_A; + } else { + s->state = SSL3_ST_CR_CHANGE; + } + + ssl_free_wbio_buffer(s); + ret = 1; + goto end; + + case SSL_ST_OK: + /* clean a few things up */ + ssl3_cleanup_key_block(s); + + if (s->init_buf != NULL) { + BUF_MEM_free(s->init_buf); + s->init_buf = NULL; + } + + /* Remove write buffering now. */ + ssl_free_wbio_buffer(s); + + s->init_num = 0; + s->renegotiate = 0; + s->new_session = 0; + + ssl_update_cache(s, SSL_SESS_CACHE_CLIENT); + if (s->hit) { + s->ctx->stats.sess_hit++; + } + + ret = 1; + /* s->server=0; */ + s->ctx->stats.sess_connect_good++; + + if (cb != NULL) { + cb(s, SSL_CB_HANDSHAKE_DONE, 1); + } + + goto end; + + default: + OPENSSL_PUT_ERROR(SSL, ssl3_connect, SSL_R_UNKNOWN_STATE); + ret = -1; + goto end; + } + + if (!s->s3->tmp.reuse_message && !skip) { + if (cb != NULL && s->state != state) { + new_state = s->state; + s->state = state; + cb(s, SSL_CB_CONNECT_LOOP, 1); + s->state = new_state; + } + } + skip = 0; + } + +end: + s->in_handshake--; + if (buf != NULL) { + BUF_MEM_free(buf); + } + if (cb != NULL) { + cb(s, SSL_CB_CONNECT_EXIT, ret); + } + return ret; +} + +int ssl3_send_client_hello(SSL *s) { + uint8_t *buf, *p, *d; + int i; + unsigned long l; + + buf = (uint8_t *)s->init_buf->data; + if (s->state == SSL3_ST_CW_CLNT_HELLO_A) { + if (!s->s3->have_version) { + uint16_t max_version = ssl3_get_max_client_version(s); + /* Disabling all versions is silly: return an error. */ + if (max_version == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, SSL_R_WRONG_SSL_VERSION); + goto err; + } + s->version = max_version; + s->client_version = max_version; + } + + /* If the configured session was created at a version higher than our + * maximum version, drop it. */ + if (s->session && + (s->session->session_id_length == 0 || s->session->not_resumable || + (!SSL_IS_DTLS(s) && s->session->ssl_version > s->version) || + (SSL_IS_DTLS(s) && s->session->ssl_version < s->version))) { + SSL_set_session(s, NULL); + } + + /* else use the pre-loaded session */ + p = s->s3->client_random; + + /* If resending the ClientHello in DTLS after a HelloVerifyRequest, don't + * renegerate the client_random. The random must be reused. */ + if (!SSL_IS_DTLS(s) || !s->d1->send_cookie) { + ssl_fill_hello_random(s, 0, p, sizeof(s->s3->client_random)); + } + + /* Do the message type and length last. Note: the final argument to + * ssl_add_clienthello_tlsext below depends on the size of this prefix. */ + d = p = ssl_handshake_start(s); + + /* version indicates the negotiated version: for example from an SSLv2/v3 + * compatible client hello). The client_version field is the maximum + * version we permit and it is also used in RSA encrypted premaster + * secrets. Some servers can choke if we initially report a higher version + * then renegotiate to a lower one in the premaster secret. This didn't + * happen with TLS 1.0 as most servers supported it but it can with TLS 1.1 + * or later if the server only supports 1.0. + * + * Possible scenario with previous logic: + * 1. Client hello indicates TLS 1.2 + * 2. Server hello says TLS 1.0 + * 3. RSA encrypted premaster secret uses 1.2. + * 4. Handhaked proceeds using TLS 1.0. + * 5. Server sends hello request to renegotiate. + * 6. Client hello indicates TLS v1.0 as we now + * know that is maximum server supports. + * 7. Server chokes on RSA encrypted premaster secret + * containing version 1.0. + * + * For interoperability it should be OK to always use the maximum version + * we support in client hello and then rely on the checking of version to + * ensure the servers isn't being inconsistent: for example initially + * negotiating with TLS 1.0 and renegotiating with TLS 1.2. We do this by + * using client_version in client hello and not resetting it to the + * negotiated version. */ + *(p++) = s->client_version >> 8; + *(p++) = s->client_version & 0xff; + + /* Random stuff */ + memcpy(p, s->s3->client_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + + /* Session ID */ + if (s->new_session || s->session == NULL) { + i = 0; + } else { + i = s->session->session_id_length; + } + *(p++) = i; + if (i != 0) { + if (i > (int)sizeof(s->session->session_id)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, ERR_R_INTERNAL_ERROR); + goto err; + } + memcpy(p, s->session->session_id, i); + p += i; + } + + /* cookie stuff for DTLS */ + if (SSL_IS_DTLS(s)) { + if (s->d1->cookie_len > sizeof(s->d1->cookie)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, ERR_R_INTERNAL_ERROR); + goto err; + } + *(p++) = s->d1->cookie_len; + memcpy(p, s->d1->cookie, s->d1->cookie_len); + p += s->d1->cookie_len; + } + + /* Ciphers supported */ + i = ssl_cipher_list_to_bytes(s, SSL_get_ciphers(s), &p[2]); + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, + SSL_R_NO_CIPHERS_AVAILABLE); + goto err; + } + s2n(i, p); + p += i; + + /* COMPRESSION */ + *(p++) = 1; + *(p++) = 0; /* Add the NULL method */ + + /* TLS extensions*/ + if (ssl_prepare_clienthello_tlsext(s) <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, SSL_R_CLIENTHELLO_TLSEXT); + goto err; + } + + p = ssl_add_clienthello_tlsext(s, p, buf + SSL3_RT_MAX_PLAIN_LENGTH, + p - buf); + if (p == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_hello, ERR_R_INTERNAL_ERROR); + goto err; + } + + l = p - d; + ssl_set_handshake_header(s, SSL3_MT_CLIENT_HELLO, l); + s->state = SSL3_ST_CW_CLNT_HELLO_B; + } + + /* SSL3_ST_CW_CLNT_HELLO_B */ + return ssl_do_write(s); + +err: + return -1; +} + +int ssl3_get_server_hello(SSL *s) { + STACK_OF(SSL_CIPHER) * sk; + const SSL_CIPHER *c; + CERT *ct = s->cert; + int al = SSL_AD_INTERNAL_ERROR, ok; + long n; + CBS server_hello, server_random, session_id; + uint16_t server_version, cipher_suite; + uint8_t compression_method; + unsigned long mask_ssl; + + n = s->method->ssl_get_message(s, SSL3_ST_CR_SRVR_HELLO_A, + SSL3_ST_CR_SRVR_HELLO_B, SSL3_MT_SERVER_HELLO, + 20000, /* ?? */ + SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + 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, ssl3_get_server_hello, + SSL_R_HANDSHAKE_FAILURE_ON_CLIENT_HELLO); + } + return n; + } + + CBS_init(&server_hello, s->init_msg, n); + + if (!CBS_get_u16(&server_hello, &server_version) || + !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, ssl3_get_server_hello, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (!s->s3->have_version) { + if (!ssl3_is_version_enabled(s, server_version)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_UNSUPPORTED_PROTOCOL); + s->version = server_version; + /* Mark the version as fixed so the record-layer version is not clamped + * to TLS 1.0. */ + s->s3->have_version = 1; + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + s->version = server_version; + s->enc_method = ssl3_get_enc_method(server_version); + assert(s->enc_method != NULL); + /* At this point, the connection's version is known and s->version is + * fixed. Begin enforcing the record-layer version. */ + s->s3->have_version = 1; + } else if (server_version != s->version) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_WRONG_SSL_VERSION); + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + + /* Copy over the server random. */ + memcpy(s->s3->server_random, CBS_data(&server_random), SSL3_RANDOM_SIZE); + + assert(s->session == NULL || s->session->session_id_length > 0); + if (s->session != NULL && CBS_mem_equal(&session_id, s->session->session_id, + s->session->session_id_length)) { + if (s->sid_ctx_length != s->session->sid_ctx_length || + memcmp(s->session->sid_ctx, s->sid_ctx, s->sid_ctx_length)) { + /* actually a client application bug */ + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, + SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT); + goto f_err; + } + s->hit = 1; + } else { + /* The session wasn't resumed. Create a fresh SSL_SESSION to + * fill out. */ + s->hit = 0; + if (!ssl_get_new_session(s, 0)) { + goto f_err; + } + /* Note: session_id could be empty. */ + s->session->session_id_length = CBS_len(&session_id); + memcpy(s->session->session_id, CBS_data(&session_id), CBS_len(&session_id)); + } + + c = ssl3_get_cipher_by_value(cipher_suite); + if (c == NULL) { + /* unknown cipher */ + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, + SSL_R_UNKNOWN_CIPHER_RETURNED); + goto f_err; + } + /* ct->mask_ssl was computed from client capabilities. Now + * that the final version is known, compute a new mask_ssl. */ + if (!SSL_USE_TLS1_2_CIPHERS(s)) { + mask_ssl = SSL_TLSV1_2; + } else { + mask_ssl = 0; + } + /* If the cipher is disabled then we didn't sent it in the ClientHello, so if + * the server selected it, it's an error. */ + if ((c->algorithm_ssl & mask_ssl) || + (c->algorithm_mkey & ct->mask_k) || + (c->algorithm_auth & ct->mask_a)) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_WRONG_CIPHER_RETURNED); + goto f_err; + } + + sk = ssl_get_ciphers_by_id(s); + if (!sk_SSL_CIPHER_find(sk, NULL, c)) { + /* we did not say we would use this cipher */ + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, SSL_R_WRONG_CIPHER_RETURNED); + goto f_err; + } + + if (s->hit && s->session->cipher != c) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, + SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED); + goto f_err; + } + s->s3->tmp.new_cipher = c; + + /* Most clients also require that the negotiated version match the session's + * version if resuming. However OpenSSL has historically not had the + * corresponding logic on the server, so this may not be compatible, + * depending on other factors. (Whether the ClientHello version is clamped to + * the session's version and whether the session cache is keyed on IP + * address.) + * + * TODO(davidben): See if we can still enforce this? Perhaps for the future + * TLS 1.3 and forward if this is fixed upstream. */ + + /* Don't digest cached records if no sigalgs: we may need them for client + * authentication. */ + if (!SSL_USE_SIGALGS(s) && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + goto f_err; + } + + /* Only the NULL compression algorithm is supported. */ + if (compression_method != 0) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, + SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM); + goto f_err; + } + + /* TLS extensions */ + if (!ssl_parse_serverhello_tlsext(s, &server_hello)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_hello, 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, ssl3_get_server_hello, SSL_R_BAD_PACKET_LENGTH); + goto f_err; + } + + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); +err: + return -1; +} + +int ssl3_get_server_certificate(SSL *s) { + int al, i, ok, ret = -1; + unsigned long n; + X509 *x = NULL; + STACK_OF(X509) *sk = NULL; + SESS_CERT *sc; + EVP_PKEY *pkey = NULL; + CBS cbs, certificate_list; + const uint8_t *data; + + n = s->method->ssl_get_message(s, SSL3_ST_CR_CERT_A, SSL3_ST_CR_CERT_B, + SSL3_MT_CERTIFICATE, s->max_cert_list, + SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + CBS_init(&cbs, s->init_msg, n); + + sk = sk_X509_new_null(); + if (sk == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CBS_get_u24_length_prefixed(&cbs, &certificate_list) || + CBS_len(&cbs) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, SSL_R_LENGTH_MISMATCH); + goto f_err; + } + + while (CBS_len(&certificate_list) > 0) { + CBS certificate; + if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, + SSL_R_CERT_LENGTH_MISMATCH); + goto f_err; + } + data = CBS_data(&certificate); + x = d2i_X509(NULL, &data, CBS_len(&certificate)); + if (x == NULL) { + al = SSL_AD_BAD_CERTIFICATE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, ERR_R_ASN1_LIB); + goto f_err; + } + if (data != CBS_data(&certificate) + CBS_len(&certificate)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, + SSL_R_CERT_LENGTH_MISMATCH); + goto f_err; + } + if (!sk_X509_push(sk, x)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, ERR_R_MALLOC_FAILURE); + goto err; + } + x = NULL; + } + + i = ssl_verify_cert_chain(s, sk); + if (s->verify_mode != SSL_VERIFY_NONE && i <= 0) { + al = ssl_verify_alarm_type(s->verify_result); + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, + SSL_R_CERTIFICATE_VERIFY_FAILED); + goto f_err; + } + ERR_clear_error(); /* but we keep s->verify_result */ + + sc = ssl_sess_cert_new(); + if (sc == NULL) { + goto err; + } + + if (s->session->sess_cert) { + ssl_sess_cert_free(s->session->sess_cert); + } + s->session->sess_cert = sc; + + sc->cert_chain = sk; + /* Inconsistency alert: cert_chain does include the peer's certificate, which + * we don't include in s3_srvr.c */ + x = sk_X509_value(sk, 0); + sk = NULL; + /* VRS 19990621: possible memory leak; sk=null ==> !sk_pop_free() @end*/ + + pkey = X509_get_pubkey(x); + + if (pkey == NULL || EVP_PKEY_missing_parameters(pkey)) { + x = NULL; + al = SSL3_AL_FATAL; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, + SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS); + goto f_err; + } + + i = ssl_cert_type(pkey); + if (i < 0) { + x = NULL; + al = SSL3_AL_FATAL; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, + SSL_R_UNKNOWN_CERTIFICATE_TYPE); + goto f_err; + } + + int exp_idx = ssl_cipher_get_cert_index(s->s3->tmp.new_cipher); + if (exp_idx >= 0 && i != exp_idx) { + x = NULL; + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, + SSL_R_WRONG_CERTIFICATE_TYPE); + goto f_err; + } + sc->peer_cert_type = i; + /* Why would the following ever happen? We just created sc a couple of lines + * ago. */ + if (sc->peer_pkeys[i].x509 != NULL) { + X509_free(sc->peer_pkeys[i].x509); + } + sc->peer_pkeys[i].x509 = X509_up_ref(x); + sc->peer_key = &(sc->peer_pkeys[i]); + + if (s->session->peer != NULL) { + X509_free(s->session->peer); + } + s->session->peer = X509_up_ref(x); + + s->session->verify_result = s->verify_result; + + x = NULL; + ret = 1; + + if (0) { + f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + } + +err: + EVP_PKEY_free(pkey); + X509_free(x); + sk_X509_pop_free(sk, X509_free); + return ret; +} + +int ssl3_get_server_key_exchange(SSL *s) { + EVP_MD_CTX md_ctx; + int al, ok; + long n, alg_k, alg_a; + EVP_PKEY *pkey = NULL; + const EVP_MD *md = NULL; + RSA *rsa = NULL; + DH *dh = NULL; + EC_KEY *ecdh = NULL; + BN_CTX *bn_ctx = NULL; + EC_POINT *srvr_ecpoint = NULL; + CBS server_key_exchange, server_key_exchange_orig, parameter; + + /* use same message size as in ssl3_get_certificate_request() as + * ServerKeyExchange message may be skipped */ + n = s->method->ssl_get_message(s, SSL3_ST_CR_KEY_EXCH_A, + SSL3_ST_CR_KEY_EXCH_B, -1, s->max_cert_list, + SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + if (!ok) { + return n; + } + + if (s->s3->tmp.message_type != SSL3_MT_SERVER_KEY_EXCHANGE) { + if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + SSL_R_UNEXPECTED_MESSAGE); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + return -1; + } + + /* In plain PSK ciphersuite, ServerKeyExchange can be + omitted if no identity hint is sent. Set session->sess_cert anyway to + avoid problems later.*/ + if (s->s3->tmp.new_cipher->algorithm_auth & SSL_aPSK) { + /* PSK ciphersuites that also send a Certificate would have already + * initialized |sess_cert|. */ + if (s->session->sess_cert == NULL) { + s->session->sess_cert = ssl_sess_cert_new(); + } + + /* TODO(davidben): This should be reset in one place with the rest of the + * handshake state. */ + if (s->s3->tmp.peer_psk_identity_hint) { + OPENSSL_free(s->s3->tmp.peer_psk_identity_hint); + s->s3->tmp.peer_psk_identity_hint = NULL; + } + } + s->s3->tmp.reuse_message = 1; + return 1; + } + + /* Retain a copy of the original CBS to compute the signature over. */ + CBS_init(&server_key_exchange, s->init_msg, n); + server_key_exchange_orig = server_key_exchange; + + if (s->session->sess_cert != NULL) { + if (s->session->sess_cert->peer_dh_tmp) { + DH_free(s->session->sess_cert->peer_dh_tmp); + s->session->sess_cert->peer_dh_tmp = NULL; + } + if (s->session->sess_cert->peer_ecdh_tmp) { + EC_KEY_free(s->session->sess_cert->peer_ecdh_tmp); + s->session->sess_cert->peer_ecdh_tmp = NULL; + } + } else { + s->session->sess_cert = ssl_sess_cert_new(); + } + + alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + alg_a = s->s3->tmp.new_cipher->algorithm_auth; + EVP_MD_CTX_init(&md_ctx); + + 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, ssl3_get_server_key_exchange, 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, ssl3_get_server_key_exchange, + SSL_R_DATA_LENGTH_TOO_LONG); + goto f_err; + } + + /* Save the identity hint as a C string. */ + if (!CBS_strdup(&psk_identity_hint, &s->s3->tmp.peer_psk_identity_hint)) { + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + ERR_R_MALLOC_FAILURE); + goto f_err; + } + } + + if (alg_k & SSL_kEDH) { + 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, ssl3_get_server_key_exchange, SSL_R_DECODE_ERROR); + goto f_err; + } + + dh = DH_new(); + if (dh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, ERR_R_DH_LIB); + goto err; + } + + if ((dh->p = BN_bin2bn(CBS_data(&dh_p), CBS_len(&dh_p), NULL)) == NULL || + (dh->g = BN_bin2bn(CBS_data(&dh_g), CBS_len(&dh_g), NULL)) == NULL || + (dh->pub_key = BN_bin2bn(CBS_data(&dh_Ys), CBS_len(&dh_Ys), NULL)) == + NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, ERR_R_BN_LIB); + goto err; + } + + if (DH_size(dh) < 512 / 8) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + SSL_R_BAD_DH_P_LENGTH); + goto err; + } + + if (alg_a & SSL_aRSA) { + pkey = X509_get_pubkey( + s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509); + } + /* else anonymous DH, so no certificate or pkey. */ + + s->session->sess_cert->peer_dh_tmp = dh; + dh = NULL; + } else if (alg_k & SSL_kEECDH) { + uint16_t curve_id; + int curve_nid = 0; + EC_GROUP *ngroup; + const EC_GROUP *group; + CBS point; + + /* Extract elliptic curve parameters and the server's ephemeral ECDH public + * key. Check curve is one of our preferences, if not server has sent an + * invalid curve. */ + if (!tls1_check_curve(s, &server_key_exchange, &curve_id)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_WRONG_CURVE); + goto f_err; + } + + curve_nid = tls1_ec_curve_id2nid(curve_id); + if (curve_nid == 0) { + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS); + goto f_err; + } + + ecdh = EC_KEY_new(); + if (ecdh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + ngroup = EC_GROUP_new_by_curve_name(curve_nid); + if (ngroup == NULL || + EC_KEY_set_group(ecdh, ngroup) == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, ERR_R_EC_LIB); + goto err; + } + EC_GROUP_free(ngroup); + + group = EC_KEY_get0_group(ecdh); + + /* Next, get the encoded ECPoint */ + if (!CBS_get_u8_length_prefixed(&server_key_exchange, &point)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (((srvr_ecpoint = EC_POINT_new(group)) == NULL) || + ((bn_ctx = BN_CTX_new()) == NULL)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!EC_POINT_oct2point(group, srvr_ecpoint, CBS_data(&point), + CBS_len(&point), bn_ctx)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_BAD_ECPOINT); + goto f_err; + } + + /* The ECC/TLS specification does not mention the use of DSA to sign + * ECParameters in the server key exchange message. We do support RSA and + * ECDSA. */ + if (alg_a & SSL_aRSA) { + pkey = X509_get_pubkey( + s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509); + } else if (alg_a & SSL_aECDSA) { + pkey = + X509_get_pubkey(s->session->sess_cert->peer_pkeys[SSL_PKEY_ECC].x509); + } + /* else anonymous ECDH, so no certificate or pkey. */ + EC_KEY_set_public_key(ecdh, srvr_ecpoint); + s->session->sess_cert->peer_ecdh_tmp = ecdh; + ecdh = NULL; + BN_CTX_free(bn_ctx); + bn_ctx = NULL; + EC_POINT_free(srvr_ecpoint); + srvr_ecpoint = NULL; + } else if (!(alg_k & SSL_kPSK)) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + 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_init(¶meter, CBS_data(&server_key_exchange_orig), + CBS_len(&server_key_exchange_orig) - CBS_len(&server_key_exchange)); + + /* if it was signed, check the signature */ + if (pkey != NULL) { + CBS signature; + + if (SSL_USE_SIGALGS(s)) { + if (!tls12_check_peer_sigalg(&md, &al, s, &server_key_exchange, pkey)) { + goto f_err; + } + } else if (pkey->type == EVP_PKEY_RSA) { + md = EVP_md5_sha1(); + } else { + md = EVP_sha1(); + } + + /* The last field in |server_key_exchange| is the 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, ssl3_get_server_key_exchange, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (!EVP_DigestVerifyInit(&md_ctx, NULL, md, NULL, pkey) || + !EVP_DigestVerifyUpdate(&md_ctx, s->s3->client_random, + SSL3_RANDOM_SIZE) || + !EVP_DigestVerifyUpdate(&md_ctx, s->s3->server_random, + SSL3_RANDOM_SIZE) || + !EVP_DigestVerifyUpdate(&md_ctx, CBS_data(¶meter), + CBS_len(¶meter)) || + !EVP_DigestVerifyFinal(&md_ctx, CBS_data(&signature), + CBS_len(&signature))) { + /* bad signature */ + al = SSL_AD_DECRYPT_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, SSL_R_BAD_SIGNATURE); + goto f_err; + } + } else { + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + /* Might be wrong key type, check it */ + if (ssl3_check_cert_and_algorithm(s)) { + /* Otherwise this shouldn't happen */ + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + ERR_R_INTERNAL_ERROR); + } + goto err; + } + /* still data left over */ + if (CBS_len(&server_key_exchange) > 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_key_exchange, + SSL_R_EXTRA_DATA_IN_MESSAGE); + goto f_err; + } + } + EVP_PKEY_free(pkey); + EVP_MD_CTX_cleanup(&md_ctx); + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); +err: + EVP_PKEY_free(pkey); + if (rsa != NULL) { + RSA_free(rsa); + } + if (dh != NULL) { + DH_free(dh); + } + BN_CTX_free(bn_ctx); + EC_POINT_free(srvr_ecpoint); + if (ecdh != NULL) { + EC_KEY_free(ecdh); + } + EVP_MD_CTX_cleanup(&md_ctx); + return -1; +} + +static int ca_dn_cmp(const X509_NAME **a, const X509_NAME **b) { + return X509_NAME_cmp(*a, *b); +} + +int ssl3_get_certificate_request(SSL *s) { + int ok, ret = 0; + unsigned long n; + X509_NAME *xn = NULL; + STACK_OF(X509_NAME) *ca_sk = NULL; + CBS cbs; + CBS certificate_types; + CBS certificate_authorities; + const uint8_t *data; + + n = s->method->ssl_get_message(s, SSL3_ST_CR_CERT_REQ_A, + SSL3_ST_CR_CERT_REQ_B, -1, s->max_cert_list, + SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + s->s3->tmp.cert_req = 0; + + if (s->s3->tmp.message_type == SSL3_MT_SERVER_DONE) { + s->s3->tmp.reuse_message = 1; + /* If we get here we don't need any cached handshake records as we wont be + * doing client auth. */ + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + goto err; + } + return 1; + } + + if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, + SSL_R_WRONG_MESSAGE_TYPE); + goto err; + } + + /* TLS does not like anon-DH with client cert */ + if (s->version > SSL3_VERSION && + (s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL)) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, + SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER); + goto err; + } + + CBS_init(&cbs, s->init_msg, n); + + ca_sk = sk_X509_NAME_new(ca_dn_cmp); + if (ca_sk == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* get the certificate types */ + if (!CBS_get_u8_length_prefixed(&cbs, &certificate_types)) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, SSL_R_DECODE_ERROR); + goto err; + } + + if (!CBS_stow(&certificate_types, &s->s3->tmp.certificate_types, + &s->s3->tmp.num_certificate_types)) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + goto err; + } + + if (SSL_USE_SIGALGS(s)) { + CBS supported_signature_algorithms; + if (!CBS_get_u16_length_prefixed(&cbs, &supported_signature_algorithms)) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, SSL_R_DECODE_ERROR); + goto err; + } + + if (!tls1_process_sigalgs(s, &supported_signature_algorithms)) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, + SSL_R_SIGNATURE_ALGORITHMS_ERROR); + goto err; + } + } + + /* get the CA RDNs */ + if (!CBS_get_u16_length_prefixed(&cbs, &certificate_authorities)) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, SSL_R_LENGTH_MISMATCH); + goto err; + } + + while (CBS_len(&certificate_authorities) > 0) { + CBS distinguished_name; + if (!CBS_get_u16_length_prefixed(&certificate_authorities, + &distinguished_name)) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, + SSL_R_CA_DN_TOO_LONG); + goto err; + } + + data = CBS_data(&distinguished_name); + + xn = d2i_X509_NAME(NULL, &data, CBS_len(&distinguished_name)); + if (xn == NULL) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, ERR_R_ASN1_LIB); + goto err; + } + + if (!CBS_skip(&distinguished_name, data - CBS_data(&distinguished_name))) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_certificate, ERR_R_INTERNAL_ERROR); + goto err; + } + + if (CBS_len(&distinguished_name) != 0) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, + SSL_R_CA_DN_LENGTH_MISMATCH); + goto err; + } + + if (!sk_X509_NAME_push(ca_sk, xn)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_certificate_request, + ERR_R_MALLOC_FAILURE); + goto err; + } + } + + /* we should setup a certificate to return.... */ + s->s3->tmp.cert_req = 1; + if (s->s3->tmp.ca_names != NULL) { + sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free); + } + s->s3->tmp.ca_names = ca_sk; + ca_sk = NULL; + + ret = 1; + +err: + if (ca_sk != NULL) { + sk_X509_NAME_pop_free(ca_sk, X509_NAME_free); + } + return ret; +} + +int ssl3_get_new_session_ticket(SSL *s) { + int ok, al, ret = 0; + long n; + CBS new_session_ticket, ticket; + + n = s->method->ssl_get_message( + s, SSL3_ST_CR_SESSION_TICKET_A, SSL3_ST_CR_SESSION_TICKET_B, + SSL3_MT_NEWSESSION_TICKET, 16384, SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + CBS_init(&new_session_ticket, s->init_msg, n); + + if (!CBS_get_u32(&new_session_ticket, + &s->session->tlsext_tick_lifetime_hint) || + !CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) || + CBS_len(&new_session_ticket) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_new_session_ticket, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (!CBS_stow(&ticket, &s->session->tlsext_tick, + &s->session->tlsext_ticklen)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_new_session_ticket, ERR_R_MALLOC_FAILURE); + goto err; + } + + /* There are two ways to detect a resumed ticket sesion. One is to set an + * appropriate session ID and then the server must return a match in + * ServerHello. This allows the normal client session ID matching to work and + * we know much earlier that the ticket has been accepted. + * + * The other way is to set zero length session ID when the ticket is + * presented and rely on the handshake to determine session resumption. + * + * We choose the former approach because this fits in with assumptions + * elsewhere in OpenSSL. The session ID is set to the SHA256 (or SHA1 is + * SHA256 is disabled) hash of the ticket. */ + EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), s->session->session_id, + &s->session->session_id_length, EVP_sha256(), NULL); + ret = 1; + return ret; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); +err: + return -1; +} + +int ssl3_get_cert_status(SSL *s) { + int ok, al; + long n; + CBS certificate_status, ocsp_response; + uint8_t status_type; + + n = s->method->ssl_get_message( + s, SSL3_ST_CR_CERT_STATUS_A, SSL3_ST_CR_CERT_STATUS_B, + SSL3_MT_CERTIFICATE_STATUS, 16384, SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + CBS_init(&certificate_status, s->init_msg, n); + 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, ssl3_get_cert_status, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (!CBS_stow(&ocsp_response, &s->session->ocsp_response, + &s->session->ocsp_response_length)) { + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_status, ERR_R_MALLOC_FAILURE); + goto f_err; + } + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + return -1; +} + +int ssl3_get_server_done(SSL *s) { + int ok; + long n; + + n = s->method->ssl_get_message(s, SSL3_ST_CR_SRVR_DONE_A, + SSL3_ST_CR_SRVR_DONE_B, SSL3_MT_SERVER_DONE, + 30, /* should be very small, like 0 :-) */ + SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + if (n > 0) { + /* should contain no data */ + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, ssl3_get_server_done, SSL_R_LENGTH_MISMATCH); + return -1; + } + + return 1; +} + + +int ssl3_send_client_key_exchange(SSL *s) { + uint8_t *p; + int n = 0; + unsigned long alg_k; + unsigned long alg_a; + uint8_t *q; + EVP_PKEY *pkey = NULL; + EC_KEY *clnt_ecdh = NULL; + const EC_POINT *srvr_ecpoint = NULL; + EVP_PKEY *srvr_pub_pkey = NULL; + uint8_t *encodedPoint = NULL; + int encoded_pt_len = 0; + BN_CTX *bn_ctx = NULL; + unsigned int psk_len = 0; + uint8_t psk[PSK_MAX_PSK_LEN]; + uint8_t *pms = NULL; + size_t pms_len = 0; + + if (s->state == SSL3_ST_CW_KEY_EXCH_A) { + p = ssl_handshake_start(s); + + alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + alg_a = s->s3->tmp.new_cipher->algorithm_auth; + + /* If using a PSK key exchange, prepare the pre-shared key. */ + if (alg_a & SSL_aPSK) { + char identity[PSK_MAX_IDENTITY_LEN + 1]; + size_t identity_len; + + if (s->psk_client_callback == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + SSL_R_PSK_NO_CLIENT_CB); + goto err; + } + + memset(identity, 0, sizeof(identity)); + psk_len = + s->psk_client_callback(s, s->s3->tmp.peer_psk_identity_hint, identity, + sizeof(identity), psk, sizeof(psk)); + if (psk_len > PSK_MAX_PSK_LEN) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } else if (psk_len == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + SSL_R_PSK_IDENTITY_NOT_FOUND); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + goto err; + } + + identity_len = OPENSSL_strnlen(identity, sizeof(identity)); + if (identity_len > PSK_MAX_IDENTITY_LEN) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } + + if (s->session->psk_identity != NULL) { + OPENSSL_free(s->session->psk_identity); + } + + s->session->psk_identity = BUF_strdup(identity); + if (s->session->psk_identity == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + /* Write out psk_identity. */ + s2n(identity_len, p); + memcpy(p, identity, identity_len); + p += identity_len; + n = 2 + identity_len; + } + + /* Depending on the key exchange method, compute |pms| and |pms_len|. */ + if (alg_k & SSL_kRSA) { + RSA *rsa; + size_t enc_pms_len; + + pms_len = SSL_MAX_MASTER_KEY_LENGTH; + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (s->session->sess_cert == NULL) { + /* We should always have a server certificate with SSL_kRSA. */ + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } + + pkey = X509_get_pubkey( + s->session->sess_cert->peer_pkeys[SSL_PKEY_RSA_ENC].x509); + if (pkey == NULL || + pkey->type != EVP_PKEY_RSA || + pkey->pkey.rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_INTERNAL_ERROR); + if (pkey != NULL) { + EVP_PKEY_free(pkey); + } + goto err; + } + + rsa = pkey->pkey.rsa; + EVP_PKEY_free(pkey); + + pms[0] = s->client_version >> 8; + pms[1] = s->client_version & 0xff; + if (!RAND_bytes(&pms[2], SSL_MAX_MASTER_KEY_LENGTH - 2)) { + goto err; + } + + s->session->master_key_length = SSL_MAX_MASTER_KEY_LENGTH; + + q = p; + /* In TLS and beyond, reserve space for the length prefix. */ + if (s->version > SSL3_VERSION) { + p += 2; + n += 2; + } + if (!RSA_encrypt(rsa, &enc_pms_len, p, RSA_size(rsa), pms, pms_len, + RSA_PKCS1_PADDING)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + SSL_R_BAD_RSA_ENCRYPT); + goto err; + } + n += enc_pms_len; + + /* Log the premaster secret, if logging is enabled. */ + if (!ssl_ctx_log_rsa_client_key_exchange(s->ctx, p, enc_pms_len, pms, + pms_len)) { + goto err; + } + + /* Fill in the length prefix. */ + if (s->version > SSL3_VERSION) { + s2n(enc_pms_len, q); + } + } else if (alg_k & SSL_kEDH) { + DH *dh_srvr, *dh_clnt; + SESS_CERT *scert = s->session->sess_cert; + int dh_len; + size_t pub_len; + + if (scert == NULL) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + SSL_R_UNEXPECTED_MESSAGE); + goto err; + } + + if (scert->peer_dh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } + dh_srvr = scert->peer_dh_tmp; + + /* generate a new random key */ + dh_clnt = DHparams_dup(dh_srvr); + if (dh_clnt == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB); + goto err; + } + if (!DH_generate_key(dh_clnt)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB); + DH_free(dh_clnt); + goto err; + } + + pms_len = DH_size(dh_clnt); + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_MALLOC_FAILURE); + DH_free(dh_clnt); + goto err; + } + + dh_len = DH_compute_key(pms, dh_srvr->pub_key, dh_clnt); + if (dh_len <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB); + DH_free(dh_clnt); + goto err; + } + pms_len = dh_len; + + /* send off the data */ + pub_len = BN_num_bytes(dh_clnt->pub_key); + s2n(pub_len, p); + BN_bn2bin(dh_clnt->pub_key, p); + n += 2 + pub_len; + + DH_free(dh_clnt); + } else if (alg_k & SSL_kEECDH) { + const EC_GROUP *srvr_group = NULL; + EC_KEY *tkey; + int field_size = 0, ecdh_len; + + if (s->session->sess_cert == NULL) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE); + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + SSL_R_UNEXPECTED_MESSAGE); + goto err; + } + + if (s->session->sess_cert->peer_ecdh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } + + tkey = s->session->sess_cert->peer_ecdh_tmp; + + srvr_group = EC_KEY_get0_group(tkey); + srvr_ecpoint = EC_KEY_get0_public_key(tkey); + if (srvr_group == NULL || srvr_ecpoint == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } + + clnt_ecdh = EC_KEY_new(); + if (clnt_ecdh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!EC_KEY_set_group(clnt_ecdh, srvr_group)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_EC_LIB); + goto err; + } + + /* Generate a new ECDH key pair */ + if (!EC_KEY_generate_key(clnt_ecdh)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + + field_size = EC_GROUP_get_degree(srvr_group); + if (field_size <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + + pms_len = (field_size + 7) / 8; + pms = OPENSSL_malloc(pms_len); + if (pms == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + ecdh_len = ECDH_compute_key(pms, pms_len, srvr_ecpoint, clnt_ecdh, NULL); + if (ecdh_len <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + pms_len = ecdh_len; + + /* First check the size of encoding and allocate memory accordingly. */ + encoded_pt_len = + EC_POINT_point2oct(srvr_group, EC_KEY_get0_public_key(clnt_ecdh), + POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + + encodedPoint = + (uint8_t *)OPENSSL_malloc(encoded_pt_len * sizeof(uint8_t)); + bn_ctx = BN_CTX_new(); + if (encodedPoint == NULL || bn_ctx == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + /* Encode the public key */ + encoded_pt_len = EC_POINT_point2oct( + srvr_group, EC_KEY_get0_public_key(clnt_ecdh), + POINT_CONVERSION_UNCOMPRESSED, encodedPoint, encoded_pt_len, bn_ctx); + + *p = encoded_pt_len; /* length of encoded point */ + /* Encoded point will be copied here */ + p += 1; + n += 1; + /* copy the point */ + memcpy(p, encodedPoint, encoded_pt_len); + /* increment n to account for length field */ + n += encoded_pt_len; + + /* Free allocated memory */ + BN_CTX_free(bn_ctx); + bn_ctx = NULL; + OPENSSL_free(encodedPoint); + encodedPoint = NULL; + EC_KEY_free(clnt_ecdh); + clnt_ecdh = NULL; + EVP_PKEY_free(srvr_pub_pkey); + srvr_pub_pkey = NULL; + } 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, ssl3_send_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + memset(pms, 0, pms_len); + } else { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + 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 cbb, child; + uint8_t *new_pms; + size_t new_pms_len; + + if (!CBB_init(&cbb, 2 + psk_len + 2 + pms_len)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + if (!CBB_add_u16_length_prefixed(&cbb, &child) || + !CBB_add_bytes(&child, pms, pms_len) || + !CBB_add_u16_length_prefixed(&cbb, &child) || + !CBB_add_bytes(&child, psk, psk_len) || + !CBB_finish(&cbb, &new_pms, &new_pms_len)) { + CBB_cleanup(&cbb); + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, + ERR_R_INTERNAL_ERROR); + 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. */ + ssl_set_handshake_header(s, SSL3_MT_CLIENT_KEY_EXCHANGE, n); + s->state = SSL3_ST_CW_KEY_EXCH_B; + + s->session->master_key_length = s->enc_method->generate_master_secret( + s, s->session->master_key, pms, pms_len); + if (s->session->master_key_length == 0) { + goto err; + } + s->session->extended_master_secret = s->s3->tmp.extended_master_secret; + OPENSSL_cleanse(pms, pms_len); + OPENSSL_free(pms); + } + + /* SSL3_ST_CW_KEY_EXCH_B */ + return s->enc_method->do_write(s); + +err: + BN_CTX_free(bn_ctx); + if (encodedPoint != NULL) { + OPENSSL_free(encodedPoint); + } + if (clnt_ecdh != NULL) { + EC_KEY_free(clnt_ecdh); + } + EVP_PKEY_free(srvr_pub_pkey); + if (pms) { + OPENSSL_cleanse(pms, pms_len); + OPENSSL_free(pms); + } + return -1; +} + +int ssl3_send_cert_verify(SSL *s) { + uint8_t *buf, *p; + const EVP_MD *md = NULL; + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_length; + EVP_PKEY *pkey; + EVP_PKEY_CTX *pctx = NULL; + size_t signature_length = 0; + unsigned long n = 0; + + buf = (uint8_t *)s->init_buf->data; + + if (s->state == SSL3_ST_CW_CERT_VRFY_A) { + p = ssl_handshake_start(s); + pkey = s->cert->key->privatekey; + + /* Write out the digest type if needbe. */ + if (SSL_USE_SIGALGS(s)) { + md = tls1_choose_signing_digest(s, pkey); + if (!tls12_get_sigandhash(p, pkey, md)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_cert_verify, ERR_R_INTERNAL_ERROR); + goto err; + } + p += 2; + n += 2; + } + + /* Compute the digest. */ + if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey)) { + goto err; + } + + /* The handshake buffer is no longer necessary. */ + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + goto err; + } + + /* Sign the digest. */ + pctx = EVP_PKEY_CTX_new(pkey, NULL); + if (pctx == NULL) { + goto err; + } + + /* Initialize the EVP_PKEY_CTX and determine the size of the signature. */ + if (!EVP_PKEY_sign_init(pctx) || !EVP_PKEY_CTX_set_signature_md(pctx, md) || + !EVP_PKEY_sign(pctx, NULL, &signature_length, digest, digest_length)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_cert_verify, ERR_R_EVP_LIB); + goto err; + } + + if (p + 2 + signature_length > buf + SSL3_RT_MAX_PLAIN_LENGTH) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_cert_verify, SSL_R_DATA_LENGTH_TOO_LONG); + goto err; + } + + if (!EVP_PKEY_sign(pctx, &p[2], &signature_length, digest, digest_length)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_cert_verify, ERR_R_EVP_LIB); + goto err; + } + + s2n(signature_length, p); + n += signature_length + 2; + + ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_VERIFY, n); + s->state = SSL3_ST_CW_CERT_VRFY_B; + } + + EVP_PKEY_CTX_free(pctx); + return ssl_do_write(s); + +err: + EVP_PKEY_CTX_free(pctx); + return -1; +} + +/* ssl3_has_client_certificate returns true if a client certificate is + * configured. */ +static int ssl3_has_client_certificate(SSL *s) { + return s->cert && s->cert->key->x509 && s->cert->key->privatekey; +} + +int ssl3_send_client_certificate(SSL *s) { + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + int i; + + if (s->state == SSL3_ST_CW_CERT_A) { + /* Let cert callback update client certificates if required */ + if (s->cert->cert_cb) { + i = s->cert->cert_cb(s, s->cert->cert_cb_arg); + if (i < 0) { + s->rwstate = SSL_X509_LOOKUP; + return -1; + } + if (i == 0) { + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR); + return 0; + } + s->rwstate = SSL_NOTHING; + } + + if (ssl3_has_client_certificate(s)) { + s->state = SSL3_ST_CW_CERT_C; + } else { + s->state = SSL3_ST_CW_CERT_B; + } + } + + /* We need to get a client cert */ + if (s->state == SSL3_ST_CW_CERT_B) { + /* If we get an error, we need to: + * ssl->rwstate=SSL_X509_LOOKUP; return(-1); + * We then get retried later */ + i = ssl_do_client_cert_cb(s, &x509, &pkey); + if (i < 0) { + s->rwstate = SSL_X509_LOOKUP; + return -1; + } + s->rwstate = SSL_NOTHING; + if (i == 1 && pkey != NULL && x509 != NULL) { + s->state = SSL3_ST_CW_CERT_B; + if (!SSL_use_certificate(s, x509) || !SSL_use_PrivateKey(s, pkey)) { + i = 0; + } + } else if (i == 1) { + i = 0; + OPENSSL_PUT_ERROR(SSL, ssl3_send_client_certificate, + SSL_R_BAD_DATA_RETURNED_BY_CALLBACK); + } + + if (x509 != NULL) { + X509_free(x509); + } + if (pkey != NULL) { + EVP_PKEY_free(pkey); + } + if (i && !ssl3_has_client_certificate(s)) { + i = 0; + } + if (i == 0) { + if (s->version == SSL3_VERSION) { + s->s3->tmp.cert_req = 0; + ssl3_send_alert(s, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE); + return 1; + } else { + s->s3->tmp.cert_req = 2; + } + } + + /* Ok, we have a cert */ + s->state = SSL3_ST_CW_CERT_C; + } + + if (s->state == SSL3_ST_CW_CERT_C) { + s->state = SSL3_ST_CW_CERT_D; + ssl3_output_cert_chain(s, (s->s3->tmp.cert_req == 2) ? NULL : s->cert->key); + } + + /* SSL3_ST_CW_CERT_D */ + return ssl_do_write(s); +} + +#define has_bits(i, m) (((i) & (m)) == (m)) + +int ssl3_check_cert_and_algorithm(SSL *s) { + int i, idx; + long alg_k, alg_a; + EVP_PKEY *pkey = NULL; + SESS_CERT *sc; + DH *dh; + + /* we don't have a certificate */ + if (!ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + return 1; + } + + alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + alg_a = s->s3->tmp.new_cipher->algorithm_auth; + + sc = s->session->sess_cert; + if (sc == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, ERR_R_INTERNAL_ERROR); + goto err; + } + + dh = s->session->sess_cert->peer_dh_tmp; + + /* This is the passed certificate */ + + idx = sc->peer_cert_type; + if (idx == SSL_PKEY_ECC) { + if (ssl_check_srvr_ecc_cert_and_alg(sc->peer_pkeys[idx].x509, s) == 0) { + /* check failed */ + OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_BAD_ECC_CERT); + goto f_err; + } else { + return 1; + } + } else if (alg_a & SSL_aECDSA) { + OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, + SSL_R_MISSING_ECDSA_SIGNING_CERT); + goto f_err; + } + pkey = X509_get_pubkey(sc->peer_pkeys[idx].x509); + i = X509_certificate_type(sc->peer_pkeys[idx].x509, pkey); + EVP_PKEY_free(pkey); + + /* Check that we have a certificate if we require one */ + if ((alg_a & SSL_aRSA) && !has_bits(i, EVP_PK_RSA | EVP_PKT_SIGN)) { + OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, + SSL_R_MISSING_RSA_SIGNING_CERT); + goto f_err; + } + + if ((alg_k & SSL_kRSA) && !has_bits(i, EVP_PK_RSA | EVP_PKT_ENC)) { + OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, + SSL_R_MISSING_RSA_ENCRYPTING_CERT); + goto f_err; + } + + if ((alg_k & SSL_kEDH) && + !(has_bits(i, EVP_PK_DH | EVP_PKT_EXCH) || dh != NULL)) { + OPENSSL_PUT_ERROR(SSL, ssl3_check_cert_and_algorithm, SSL_R_MISSING_DH_KEY); + goto f_err; + } + + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); +err: + return 0; +} + +int ssl3_send_next_proto(SSL *s) { + unsigned int len, padding_len; + uint8_t *d, *p; + + if (s->state == SSL3_ST_CW_NEXT_PROTO_A) { + len = s->next_proto_negotiated_len; + padding_len = 32 - ((len + 2) % 32); + + d = p = ssl_handshake_start(s); + *(p++) = len; + memcpy(p, s->next_proto_negotiated, len); + p += len; + *(p++) = padding_len; + memset(p, 0, padding_len); + p += padding_len; + + ssl_set_handshake_header(s, SSL3_MT_NEXT_PROTO, p - d); + s->state = SSL3_ST_CW_NEXT_PROTO_B; + } + + return ssl_do_write(s); +} + +int ssl3_send_channel_id(SSL *s) { + uint8_t *d; + int ret = -1, public_key_len; + EVP_MD_CTX md_ctx; + size_t sig_len; + ECDSA_SIG *sig = NULL; + uint8_t *public_key = NULL, *derp, *der_sig = NULL; + + if (s->state != SSL3_ST_CW_CHANNEL_ID_A) { + return ssl_do_write(s); + } + + if (!s->tlsext_channel_id_private && s->ctx->channel_id_cb) { + EVP_PKEY *key = NULL; + s->ctx->channel_id_cb(s, &key); + if (key != NULL) { + s->tlsext_channel_id_private = key; + } + } + + if (!s->tlsext_channel_id_private) { + s->rwstate = SSL_CHANNEL_ID_LOOKUP; + return -1; + } + s->rwstate = SSL_NOTHING; + + d = ssl_handshake_start(s); + if (s->s3->tlsext_channel_id_new) { + s2n(TLSEXT_TYPE_channel_id_new, d); + } else { + s2n(TLSEXT_TYPE_channel_id, d); + } + s2n(TLSEXT_CHANNEL_ID_SIZE, d); + + EVP_MD_CTX_init(&md_ctx); + + public_key_len = i2d_PublicKey(s->tlsext_channel_id_private, NULL); + if (public_key_len <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, + SSL_R_CANNOT_SERIALIZE_PUBLIC_KEY); + goto err; + } + + /* i2d_PublicKey will produce an ANSI X9.62 public key which, for a + * P-256 key, is 0x04 (meaning uncompressed) followed by the x and y + * field elements as 32-byte, big-endian numbers. */ + if (public_key_len != 65) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, SSL_R_CHANNEL_ID_NOT_P256); + goto err; + } + public_key = OPENSSL_malloc(public_key_len); + if (!public_key) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, ERR_R_MALLOC_FAILURE); + goto err; + } + + derp = public_key; + i2d_PublicKey(s->tlsext_channel_id_private, &derp); + + if (EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, + s->tlsext_channel_id_private) != 1) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, + SSL_R_EVP_DIGESTSIGNINIT_FAILED); + goto err; + } + + if (!tls1_channel_id_hash(&md_ctx, s)) { + goto err; + } + + if (!EVP_DigestSignFinal(&md_ctx, NULL, &sig_len)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, + SSL_R_EVP_DIGESTSIGNFINAL_FAILED); + goto err; + } + + der_sig = OPENSSL_malloc(sig_len); + if (!der_sig) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!EVP_DigestSignFinal(&md_ctx, der_sig, &sig_len)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, + SSL_R_EVP_DIGESTSIGNFINAL_FAILED); + goto err; + } + + derp = der_sig; + sig = d2i_ECDSA_SIG(NULL, (const uint8_t **)&derp, sig_len); + if (sig == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, SSL_R_D2I_ECDSA_SIG); + goto err; + } + + /* The first byte of public_key will be 0x4, denoting an uncompressed key. */ + memcpy(d, public_key + 1, 64); + d += 64; + if (!BN_bn2bin_padded(d, 32, sig->r) || + !BN_bn2bin_padded(d + 32, 32, sig->s)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_channel_id, ERR_R_INTERNAL_ERROR); + goto err; + } + + ssl_set_handshake_header(s, SSL3_MT_ENCRYPTED_EXTENSIONS, + 2 + 2 + TLSEXT_CHANNEL_ID_SIZE); + s->state = SSL3_ST_CW_CHANNEL_ID_B; + + ret = ssl_do_write(s); + +err: + EVP_MD_CTX_cleanup(&md_ctx); + if (public_key) { + OPENSSL_free(public_key); + } + if (der_sig) { + OPENSSL_free(der_sig); + } + if (sig) { + ECDSA_SIG_free(sig); + } + + return ret; +} + +int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey) { + int i = 0; + if (s->ctx->client_cert_cb) { + i = s->ctx->client_cert_cb(s, px509, ppkey); + } + return i; +} diff --git a/src/ssl/s3_enc.c b/src/ssl/s3_enc.c new file mode 100644 index 0000000..562cb84 --- /dev/null +++ b/src/ssl/s3_enc.c @@ -0,0 +1,527 @@ +/* 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. + *g + * 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). + *g + * 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. + *g + * 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) fromg + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + *g + * 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. + *g + * 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.g + * + * 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 <stdio.h> +#include <assert.h> + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/md5.h> +#include <openssl/obj.h> + +#include "ssl_locl.h" + + +static const uint8_t ssl3_pad_1[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 ssl3_pad_2[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, +}; + +static int ssl3_handshake_mac(SSL *s, int md_nid, const char *sender, int len, + uint8_t *p); + +int ssl3_prf(SSL *s, 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, ssl3_prf, 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, ssl3_prf, 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, ssl3_prf, 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); + 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; +} + +void ssl3_cleanup_key_block(SSL *s) { + if (s->s3->tmp.key_block != NULL) { + OPENSSL_cleanse(s->s3->tmp.key_block, s->s3->tmp.key_block_length); + OPENSSL_free(s->s3->tmp.key_block); + s->s3->tmp.key_block = NULL; + } + s->s3->tmp.key_block_length = 0; +} + +int ssl3_init_finished_mac(SSL *s) { + if (s->s3->handshake_buffer) { + BIO_free(s->s3->handshake_buffer); + } + if (s->s3->handshake_dgst) { + ssl3_free_digest_list(s); + } + s->s3->handshake_buffer = BIO_new(BIO_s_mem()); + if (s->s3->handshake_buffer == NULL) { + return 0; + } + BIO_set_close(s->s3->handshake_buffer, BIO_CLOSE); + + return 1; +} + +void ssl3_free_digest_list(SSL *s) { + int i; + if (!s->s3->handshake_dgst) { + return; + } + for (i = 0; i < SSL_MAX_DIGEST; i++) { + if (s->s3->handshake_dgst[i]) { + EVP_MD_CTX_destroy(s->s3->handshake_dgst[i]); + } + } + OPENSSL_free(s->s3->handshake_dgst); + s->s3->handshake_dgst = NULL; +} + +void ssl3_finish_mac(SSL *s, const uint8_t *buf, int len) { + int i; + + if (s->s3->handshake_buffer) { + BIO_write(s->s3->handshake_buffer, (void *)buf, len); + return; + } + + for (i = 0; i < SSL_MAX_DIGEST; i++) { + if (s->s3->handshake_dgst[i] != NULL) { + EVP_DigestUpdate(s->s3->handshake_dgst[i], buf, len); + } + } +} + +int ssl3_digest_cached_records( + SSL *s, enum should_free_handshake_buffer_t should_free_handshake_buffer) { + int i; + long mask; + const EVP_MD *md; + const uint8_t *hdata; + size_t hdatalen; + + /* Allocate handshake_dgst array */ + ssl3_free_digest_list(s); + s->s3->handshake_dgst = OPENSSL_malloc(SSL_MAX_DIGEST * sizeof(EVP_MD_CTX *)); + if (s->s3->handshake_dgst == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_digest_cached_records, ERR_R_MALLOC_FAILURE); + return 0; + } + + memset(s->s3->handshake_dgst, 0, SSL_MAX_DIGEST * sizeof(EVP_MD_CTX *)); + if (!BIO_mem_contents(s->s3->handshake_buffer, &hdata, &hdatalen)) { + OPENSSL_PUT_ERROR(SSL, ssl3_digest_cached_records, + SSL_R_BAD_HANDSHAKE_LENGTH); + return 0; + } + + /* Loop through bits of algorithm2 field and create MD_CTX-es */ + for (i = 0; ssl_get_handshake_digest(i, &mask, &md); i++) { + if ((mask & ssl_get_algorithm2(s)) && md) { + s->s3->handshake_dgst[i] = EVP_MD_CTX_create(); + if (s->s3->handshake_dgst[i] == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_digest_cached_records, ERR_LIB_EVP); + return 0; + } + if (!EVP_DigestInit_ex(s->s3->handshake_dgst[i], md, NULL)) { + EVP_MD_CTX_destroy(s->s3->handshake_dgst[i]); + s->s3->handshake_dgst[i] = NULL; + OPENSSL_PUT_ERROR(SSL, ssl3_digest_cached_records, ERR_LIB_EVP); + return 0; + } + EVP_DigestUpdate(s->s3->handshake_dgst[i], hdata, hdatalen); + } else { + s->s3->handshake_dgst[i] = NULL; + } + } + + if (should_free_handshake_buffer == free_handshake_buffer) { + /* Free handshake_buffer BIO */ + BIO_free(s->s3->handshake_buffer); + s->s3->handshake_buffer = NULL; + } + + return 1; +} + +int ssl3_cert_verify_mac(SSL *s, int md_nid, uint8_t *p) { + return ssl3_handshake_mac(s, md_nid, NULL, 0, p); +} + +int ssl3_final_finish_mac(SSL *s, const char *sender, int len, uint8_t *p) { + int ret, sha1len; + ret = ssl3_handshake_mac(s, NID_md5, sender, len, p); + if (ret == 0) { + return 0; + } + + p += ret; + + sha1len = ssl3_handshake_mac(s, NID_sha1, sender, len, p); + if (sha1len == 0) { + return 0; + } + + ret += sha1len; + return ret; +} + +static int ssl3_handshake_mac(SSL *s, int md_nid, const char *sender, int len, + uint8_t *p) { + unsigned int ret; + int npad, n; + unsigned int i; + uint8_t md_buf[EVP_MAX_MD_SIZE]; + EVP_MD_CTX ctx, *d = NULL; + + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + return 0; + } + + /* Search for digest of specified type in the handshake_dgst array. */ + for (i = 0; i < SSL_MAX_DIGEST; i++) { + if (s->s3->handshake_dgst[i] && + EVP_MD_CTX_type(s->s3->handshake_dgst[i]) == md_nid) { + d = s->s3->handshake_dgst[i]; + break; + } + } + + if (!d) { + OPENSSL_PUT_ERROR(SSL, ssl3_handshake_mac, SSL_R_NO_REQUIRED_DIGEST); + return 0; + } + + EVP_MD_CTX_init(&ctx); + if (!EVP_MD_CTX_copy_ex(&ctx, d)) { + EVP_MD_CTX_cleanup(&ctx); + OPENSSL_PUT_ERROR(SSL, ssl3_generate_key_block, ERR_LIB_EVP); + return 0; + } + + n = EVP_MD_CTX_size(&ctx); + if (n < 0) { + return 0; + } + + npad = (48 / n) * n; + if (sender != NULL) { + EVP_DigestUpdate(&ctx, sender, len); + } + EVP_DigestUpdate(&ctx, s->session->master_key, s->session->master_key_length); + EVP_DigestUpdate(&ctx, ssl3_pad_1, 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, ssl3_generate_key_block, ERR_LIB_EVP); + return 0; + } + EVP_DigestUpdate(&ctx, s->session->master_key, s->session->master_key_length); + EVP_DigestUpdate(&ctx, ssl3_pad_2, npad); + EVP_DigestUpdate(&ctx, md_buf, i); + EVP_DigestFinal_ex(&ctx, p, &ret); + + EVP_MD_CTX_cleanup(&ctx); + + return ret; +} + +void ssl3_record_sequence_update(uint8_t *seq) { + int i; + + for (i = 7; i >= 0; i--) { + ++seq[i]; + if (seq[i] != 0) { + break; + } + } +} + +int ssl3_alert_code(int code) { + switch (code) { + case SSL_AD_CLOSE_NOTIFY: + return SSL3_AD_CLOSE_NOTIFY; + + case SSL_AD_UNEXPECTED_MESSAGE: + return SSL3_AD_UNEXPECTED_MESSAGE; + + case SSL_AD_BAD_RECORD_MAC: + return SSL3_AD_BAD_RECORD_MAC; + + case SSL_AD_DECRYPTION_FAILED: + return SSL3_AD_BAD_RECORD_MAC; + + case SSL_AD_RECORD_OVERFLOW: + return SSL3_AD_BAD_RECORD_MAC; + + case SSL_AD_DECOMPRESSION_FAILURE: + return SSL3_AD_DECOMPRESSION_FAILURE; + + case SSL_AD_HANDSHAKE_FAILURE: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_NO_CERTIFICATE: + return SSL3_AD_NO_CERTIFICATE; + + case SSL_AD_BAD_CERTIFICATE: + return SSL3_AD_BAD_CERTIFICATE; + + case SSL_AD_UNSUPPORTED_CERTIFICATE: + return SSL3_AD_UNSUPPORTED_CERTIFICATE; + + case SSL_AD_CERTIFICATE_REVOKED: + return SSL3_AD_CERTIFICATE_REVOKED; + + case SSL_AD_CERTIFICATE_EXPIRED: + return SSL3_AD_CERTIFICATE_EXPIRED; + + case SSL_AD_CERTIFICATE_UNKNOWN: + return SSL3_AD_CERTIFICATE_UNKNOWN; + + case SSL_AD_ILLEGAL_PARAMETER: + return SSL3_AD_ILLEGAL_PARAMETER; + + case SSL_AD_UNKNOWN_CA: + return SSL3_AD_BAD_CERTIFICATE; + + case SSL_AD_ACCESS_DENIED: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_DECODE_ERROR: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_DECRYPT_ERROR: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_EXPORT_RESTRICTION: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_PROTOCOL_VERSION: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_INSUFFICIENT_SECURITY: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_INTERNAL_ERROR: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_USER_CANCELLED: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_NO_RENEGOTIATION: + return -1; /* Don't send it. */ + + case SSL_AD_UNSUPPORTED_EXTENSION: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_CERTIFICATE_UNOBTAINABLE: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_UNRECOGNIZED_NAME: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_BAD_CERTIFICATE_HASH_VALUE: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_UNKNOWN_PSK_IDENTITY: + return TLS1_AD_UNKNOWN_PSK_IDENTITY; + + case SSL_AD_INAPPROPRIATE_FALLBACK: + return SSL3_AD_INAPPROPRIATE_FALLBACK; + + default: + return -1; + } +} diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c new file mode 100644 index 0000000..9782296 --- /dev/null +++ b/src/ssl/s3_lib.c @@ -0,0 +1,1548 @@ +/* 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 <assert.h> +#include <stdio.h> + +#include <openssl/buf.h> +#include <openssl/dh.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/obj.h> + +#include "ssl_locl.h" + + +#define SSL3_NUM_CIPHERS (sizeof(ssl3_ciphers) / sizeof(SSL_CIPHER)) + +/* list of available SSLv3 ciphers (sorted by id) */ +const SSL_CIPHER ssl3_ciphers[] = { + /* The RSA ciphers */ + /* Cipher 04 */ + { + 1, SSL3_TXT_RSA_RC4_128_MD5, SSL3_CK_RSA_RC4_128_MD5, SSL_kRSA, SSL_aRSA, + SSL_RC4, SSL_MD5, SSL_SSLV3, SSL_MEDIUM, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 05 */ + { + 1, SSL3_TXT_RSA_RC4_128_SHA, SSL3_CK_RSA_RC4_128_SHA, SSL_kRSA, SSL_aRSA, + SSL_RC4, SSL_SHA1, SSL_SSLV3, SSL_MEDIUM, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 0A */ + { + 1, SSL3_TXT_RSA_DES_192_CBC3_SHA, SSL3_CK_RSA_DES_192_CBC3_SHA, SSL_kRSA, + SSL_aRSA, SSL_3DES, SSL_SHA1, SSL_SSLV3, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 112, 168, + }, + + + /* The Ephemeral DH ciphers */ + + /* Cipher 18 */ + { + 1, SSL3_TXT_ADH_RC4_128_MD5, SSL3_CK_ADH_RC4_128_MD5, SSL_kEDH, SSL_aNULL, + SSL_RC4, SSL_MD5, SSL_SSLV3, SSL_MEDIUM, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + + /* New AES ciphersuites */ + + /* Cipher 2F */ + { + 1, TLS1_TXT_RSA_WITH_AES_128_SHA, TLS1_CK_RSA_WITH_AES_128_SHA, SSL_kRSA, + SSL_aRSA, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 33 */ + { + 1, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA, TLS1_CK_DHE_RSA_WITH_AES_128_SHA, + SSL_kEDH, SSL_aRSA, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 34 */ + { + 1, TLS1_TXT_ADH_WITH_AES_128_SHA, TLS1_CK_ADH_WITH_AES_128_SHA, SSL_kEDH, + SSL_aNULL, SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 35 */ + { + 1, TLS1_TXT_RSA_WITH_AES_256_SHA, TLS1_CK_RSA_WITH_AES_256_SHA, SSL_kRSA, + SSL_aRSA, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + /* Cipher 39 */ + { + 1, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA, TLS1_CK_DHE_RSA_WITH_AES_256_SHA, + SSL_kEDH, SSL_aRSA, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + /* Cipher 3A */ + { + 1, TLS1_TXT_ADH_WITH_AES_256_SHA, TLS1_CK_ADH_WITH_AES_256_SHA, SSL_kEDH, + SSL_aNULL, SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + + /* TLS v1.2 ciphersuites */ + + /* Cipher 3C */ + { + 1, TLS1_TXT_RSA_WITH_AES_128_SHA256, TLS1_CK_RSA_WITH_AES_128_SHA256, + SSL_kRSA, SSL_aRSA, SSL_AES128, SSL_SHA256, SSL_TLSV1_2, + SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 3D */ + { + 1, TLS1_TXT_RSA_WITH_AES_256_SHA256, TLS1_CK_RSA_WITH_AES_256_SHA256, + SSL_kRSA, SSL_aRSA, SSL_AES256, SSL_SHA256, SSL_TLSV1_2, + SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + /* Cipher 67 */ + { + 1, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256, + TLS1_CK_DHE_RSA_WITH_AES_128_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES128, + SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 6B */ + { + 1, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256, + TLS1_CK_DHE_RSA_WITH_AES_256_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES256, + SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + /* Cipher 6C */ + { + 1, TLS1_TXT_ADH_WITH_AES_128_SHA256, TLS1_CK_ADH_WITH_AES_128_SHA256, + SSL_kEDH, SSL_aNULL, SSL_AES128, SSL_SHA256, SSL_TLSV1_2, + SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 6D */ + { + 1, TLS1_TXT_ADH_WITH_AES_256_SHA256, TLS1_CK_ADH_WITH_AES_256_SHA256, + SSL_kEDH, SSL_aNULL, SSL_AES256, SSL_SHA256, SSL_TLSV1_2, + SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + /* Cipher 8A */ + { + 1, TLS1_TXT_PSK_WITH_RC4_128_SHA, TLS1_CK_PSK_WITH_RC4_128_SHA, SSL_kPSK, + SSL_aPSK, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 8C */ + { + 1, 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_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher 8D */ + { + 1, 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_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + + /* GCM ciphersuites from RFC5288 */ + + /* Cipher 9C */ + { + 1, 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_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 128, 128, + }, + + /* Cipher 9D */ + { + 1, 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_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 256, 256, + }, + + /* Cipher 9E */ + { + 1, TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256, + TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES128GCM, + SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 128, 128, + }, + + /* Cipher 9F */ + { + 1, TLS1_TXT_DHE_RSA_WITH_AES_256_GCM_SHA384, + TLS1_CK_DHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kEDH, SSL_aRSA, SSL_AES256GCM, + SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 256, 256, + }, + + /* Cipher A6 */ + { + 1, TLS1_TXT_ADH_WITH_AES_128_GCM_SHA256, + TLS1_CK_ADH_WITH_AES_128_GCM_SHA256, SSL_kEDH, SSL_aNULL, SSL_AES128GCM, + SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 128, 128, + }, + + /* Cipher A7 */ + { + 1, TLS1_TXT_ADH_WITH_AES_256_GCM_SHA384, + TLS1_CK_ADH_WITH_AES_256_GCM_SHA384, SSL_kEDH, SSL_aNULL, SSL_AES256GCM, + SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 256, 256, + }, + + /* Cipher C007 */ + { + 1, TLS1_TXT_ECDHE_ECDSA_WITH_RC4_128_SHA, + TLS1_CK_ECDHE_ECDSA_WITH_RC4_128_SHA, SSL_kEECDH, SSL_aECDSA, SSL_RC4, + SSL_SHA1, SSL_TLSV1, SSL_MEDIUM, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, + 128, + }, + + /* Cipher C009 */ + { + 1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS1_CK_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, SSL_kEECDH, SSL_aECDSA, + SSL_AES128, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher C00A */ + { + 1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, SSL_kEECDH, SSL_aECDSA, + SSL_AES256, SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + /* Cipher C011 */ + { + 1, TLS1_TXT_ECDHE_RSA_WITH_RC4_128_SHA, TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA, + SSL_kEECDH, SSL_aRSA, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher C013 */ + { + 1, TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, SSL_kEECDH, SSL_aRSA, SSL_AES128, + SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher C014 */ + { + 1, TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, SSL_kEECDH, SSL_aRSA, SSL_AES256, + SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + /* Cipher C016 */ + { + 1, TLS1_TXT_ECDH_anon_WITH_RC4_128_SHA, TLS1_CK_ECDH_anon_WITH_RC4_128_SHA, + SSL_kEECDH, SSL_aNULL, SSL_RC4, SSL_SHA1, SSL_TLSV1, SSL_MEDIUM, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher C018 */ + { + 1, TLS1_TXT_ECDH_anon_WITH_AES_128_CBC_SHA, + TLS1_CK_ECDH_anon_WITH_AES_128_CBC_SHA, SSL_kEECDH, SSL_aNULL, SSL_AES128, + SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128, + }, + + /* Cipher C019 */ + { + 1, TLS1_TXT_ECDH_anon_WITH_AES_256_CBC_SHA, + TLS1_CK_ECDH_anon_WITH_AES_256_CBC_SHA, SSL_kEECDH, SSL_aNULL, SSL_AES256, + SSL_SHA1, SSL_TLSV1, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256, + }, + + + /* HMAC based TLS v1.2 ciphersuites from RFC5289 */ + + /* Cipher C023 */ + { + 1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256, + TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256, SSL_kEECDH, SSL_aECDSA, + SSL_AES128, SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128, + }, + + /* Cipher C024 */ + { + 1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384, + TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384, SSL_kEECDH, SSL_aECDSA, + SSL_AES256, SSL_SHA384, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384, 256, 256, + }, + + /* Cipher C027 */ + { + 1, TLS1_TXT_ECDHE_RSA_WITH_AES_128_SHA256, + TLS1_CK_ECDHE_RSA_WITH_AES_128_SHA256, SSL_kEECDH, SSL_aRSA, SSL_AES128, + SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128, + }, + + /* Cipher C028 */ + { + 1, TLS1_TXT_ECDHE_RSA_WITH_AES_256_SHA384, + TLS1_CK_ECDHE_RSA_WITH_AES_256_SHA384, SSL_kEECDH, SSL_aRSA, SSL_AES256, + SSL_SHA384, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384, 256, 256, + }, + + + /* GCM based TLS v1.2 ciphersuites from RFC5289 */ + + /* Cipher C02B */ + { + 1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aECDSA, + SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 128, 128, + }, + + /* Cipher C02C */ + { + 1, TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, SSL_kEECDH, SSL_aECDSA, + SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 256, 256, + }, + + /* Cipher C02F */ + { + 1, TLS1_TXT_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aRSA, + SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 128, 128, + }, + + /* Cipher C030 */ + { + 1, TLS1_TXT_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, SSL_kEECDH, SSL_aRSA, + SSL_AES256GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS, + SSL_HANDSHAKE_MAC_SHA384 | TLS1_PRF_SHA384 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 256, 256, + }, + + + /* ECDH PSK ciphersuites */ + + /* Cipher CAFE */ + { + 1, TLS1_TXT_ECDHE_PSK_WITH_AES_128_GCM_SHA256, + TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256, SSL_kEECDH, SSL_aPSK, + SSL_AES128GCM, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD | + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD, + 128, 128, + }, + + { + 1, TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, SSL_kEECDH, SSL_aRSA, + SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD, + 256, 0, + }, + + { + 1, TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, SSL_kEECDH, SSL_aECDSA, + SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD, + 256, 0, + }, + + { + 1, TLS1_TXT_DHE_RSA_WITH_CHACHA20_POLY1305, + TLS1_CK_DHE_RSA_CHACHA20_POLY1305, SSL_kEDH, SSL_aRSA, + SSL_CHACHA20POLY1305, SSL_AEAD, SSL_TLSV1_2, SSL_HIGH, + SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256 | SSL_CIPHER_ALGORITHM2_AEAD, + 256, 0, + }, +}; + +const SSL3_ENC_METHOD SSLv3_enc_data = { + tls1_enc, + ssl3_prf, + tls1_setup_key_block, + tls1_generate_master_secret, + tls1_change_cipher_state, + ssl3_final_finish_mac, + MD5_DIGEST_LENGTH+SHA_DIGEST_LENGTH, + ssl3_cert_verify_mac, + SSL3_MD_CLIENT_FINISHED_CONST, 4, + SSL3_MD_SERVER_FINISHED_CONST, 4, + ssl3_alert_code, + (int (*)(SSL *, uint8_t *, size_t, const char *, size_t, const uint8_t *, + size_t, int use_context)) ssl_undefined_function, + 0, + SSL3_HM_HEADER_LENGTH, + ssl3_set_handshake_header, + ssl3_handshake_write, +}; + +int ssl3_num_ciphers(void) { return SSL3_NUM_CIPHERS; } + +const SSL_CIPHER *ssl3_get_cipher(unsigned int u) { + if (u >= SSL3_NUM_CIPHERS) { + return NULL; + } + + return &ssl3_ciphers[SSL3_NUM_CIPHERS - 1 - u]; +} + +int ssl3_pending(const SSL *s) { + if (s->rstate == SSL_ST_READ_BODY) { + return 0; + } + + return (s->s3->rrec.type == SSL3_RT_APPLICATION_DATA) ? s->s3->rrec.length + : 0; +} + +void ssl3_set_handshake_header(SSL *s, int htype, unsigned long len) { + uint8_t *p = (uint8_t *)s->init_buf->data; + *(p++) = htype; + l2n3(len, p); + s->init_num = (int)len + SSL3_HM_HEADER_LENGTH; + s->init_off = 0; + + /* Add the message to the handshake hash. */ + ssl3_finish_mac(s, (uint8_t *)s->init_buf->data, s->init_num); +} + +int ssl3_handshake_write(SSL *s) { return ssl3_do_write(s, SSL3_RT_HANDSHAKE); } + +int ssl3_new(SSL *s) { + SSL3_STATE *s3; + + s3 = OPENSSL_malloc(sizeof *s3); + if (s3 == NULL) { + goto err; + } + memset(s3, 0, sizeof *s3); + memset(s3->rrec.seq_num, 0, sizeof(s3->rrec.seq_num)); + memset(s3->wrec.seq_num, 0, sizeof(s3->wrec.seq_num)); + + s->s3 = s3; + + /* Set the version to the highest supported version for TLS. This controls the + * initial state of |s->enc_method| and what the API reports as the version + * prior to negotiation. + * + * TODO(davidben): This is fragile and confusing. */ + s->version = TLS1_2_VERSION; + return 1; +err: + return 0; +} + +void ssl3_free(SSL *s) { + if (s == NULL || s->s3 == NULL) { + return; + } + + if (s->s3->sniff_buffer != NULL) { + BUF_MEM_free(s->s3->sniff_buffer); + } + ssl3_cleanup_key_block(s); + if (s->s3->rbuf.buf != NULL) { + ssl3_release_read_buffer(s); + } + if (s->s3->wbuf.buf != NULL) { + ssl3_release_write_buffer(s); + } + if (s->s3->tmp.dh != NULL) { + DH_free(s->s3->tmp.dh); + } + if (s->s3->tmp.ecdh != NULL) { + EC_KEY_free(s->s3->tmp.ecdh); + } + + if (s->s3->tmp.ca_names != NULL) { + sk_X509_NAME_pop_free(s->s3->tmp.ca_names, X509_NAME_free); + } + if (s->s3->tmp.certificate_types != NULL) { + OPENSSL_free(s->s3->tmp.certificate_types); + } + if (s->s3->tmp.peer_ecpointformatlist) { + OPENSSL_free(s->s3->tmp.peer_ecpointformatlist); + } + if (s->s3->tmp.peer_ellipticcurvelist) { + OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist); + } + if (s->s3->tmp.peer_psk_identity_hint) { + OPENSSL_free(s->s3->tmp.peer_psk_identity_hint); + } + if (s->s3->handshake_buffer) { + BIO_free(s->s3->handshake_buffer); + } + if (s->s3->handshake_dgst) { + ssl3_free_digest_list(s); + } + if (s->s3->alpn_selected) { + OPENSSL_free(s->s3->alpn_selected); + } + + OPENSSL_cleanse(s->s3, sizeof *s->s3); + OPENSSL_free(s->s3); + s->s3 = NULL; +} + +static int ssl3_set_req_cert_type(CERT *c, const uint8_t *p, size_t len); + +long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg) { + int ret = 0; + + if (cmd == SSL_CTRL_SET_TMP_RSA || cmd == SSL_CTRL_SET_TMP_RSA_CB || + cmd == SSL_CTRL_SET_TMP_DH || cmd == SSL_CTRL_SET_TMP_DH_CB) { + if (!ssl_cert_inst(&s->cert)) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_MALLOC_FAILURE); + return 0; + } + } + + switch (cmd) { + case SSL_CTRL_GET_SESSION_REUSED: + ret = s->hit; + break; + + case SSL_CTRL_GET_CLIENT_CERT_REQUEST: + break; + + case SSL_CTRL_GET_NUM_RENEGOTIATIONS: + ret = s->s3->num_renegotiations; + break; + + case SSL_CTRL_CLEAR_NUM_RENEGOTIATIONS: + ret = s->s3->num_renegotiations; + s->s3->num_renegotiations = 0; + break; + + case SSL_CTRL_GET_TOTAL_RENEGOTIATIONS: + ret = s->s3->total_renegotiations; + break; + + case SSL_CTRL_GET_FLAGS: + ret = (int)(s->s3->flags); + break; + + case SSL_CTRL_NEED_TMP_RSA: + /* Temporary RSA keys are never used. */ + ret = 0; + break; + + case SSL_CTRL_SET_TMP_RSA: + /* Temporary RSA keys are never used. */ + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + break; + + case SSL_CTRL_SET_TMP_RSA_CB: + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return ret; + + case SSL_CTRL_SET_TMP_DH: { + DH *dh = (DH *)parg; + if (dh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_PASSED_NULL_PARAMETER); + return ret; + } + dh = DHparams_dup(dh); + if (dh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_DH_LIB); + return ret; + } + if (!(s->options & SSL_OP_SINGLE_DH_USE) && !DH_generate_key(dh)) { + DH_free(dh); + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_DH_LIB); + return ret; + } + if (s->cert->dh_tmp != NULL) { + DH_free(s->cert->dh_tmp); + } + s->cert->dh_tmp = dh; + ret = 1; + break; + } + + case SSL_CTRL_SET_TMP_DH_CB: + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return ret; + + case SSL_CTRL_SET_TMP_ECDH: { + EC_KEY *ecdh = NULL; + + if (parg == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_PASSED_NULL_PARAMETER); + return ret; + } + if (!EC_KEY_up_ref((EC_KEY *)parg)) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_ECDH_LIB); + return ret; + } + ecdh = (EC_KEY *)parg; + if (!(s->options & SSL_OP_SINGLE_ECDH_USE) && !EC_KEY_generate_key(ecdh)) { + EC_KEY_free(ecdh); + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_ECDH_LIB); + return ret; + } + if (s->cert->ecdh_tmp != NULL) { + EC_KEY_free(s->cert->ecdh_tmp); + } + s->cert->ecdh_tmp = ecdh; + ret = 1; + break; + } + + case SSL_CTRL_SET_TMP_ECDH_CB: + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return ret; + + case SSL_CTRL_SET_TLSEXT_HOSTNAME: + if (larg == TLSEXT_NAMETYPE_host_name) { + if (s->tlsext_hostname != NULL) { + OPENSSL_free(s->tlsext_hostname); + } + s->tlsext_hostname = NULL; + + ret = 1; + if (parg == NULL) { + break; + } + if (strlen((char *)parg) > TLSEXT_MAXLEN_host_name) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, SSL_R_SSL3_EXT_INVALID_SERVERNAME); + return 0; + } + s->tlsext_hostname = BUF_strdup((char *) parg); + if (s->tlsext_hostname == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, ERR_R_INTERNAL_ERROR); + return 0; + } + } else { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, + SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE); + return 0; + } + break; + + case SSL_CTRL_SET_TLSEXT_DEBUG_ARG: + s->tlsext_debug_arg = parg; + ret = 1; + break; + + case SSL_CTRL_CHAIN: + if (larg) { + return ssl_cert_set1_chain(s->cert, (STACK_OF(X509) *)parg); + } else { + return ssl_cert_set0_chain(s->cert, (STACK_OF(X509) *)parg); + } + + case SSL_CTRL_CHAIN_CERT: + if (larg) { + return ssl_cert_add1_chain_cert(s->cert, (X509 *)parg); + } else { + return ssl_cert_add0_chain_cert(s->cert, (X509 *)parg); + } + + case SSL_CTRL_GET_CHAIN_CERTS: + *(STACK_OF(X509) **)parg = s->cert->key->chain; + break; + + case SSL_CTRL_SELECT_CURRENT_CERT: + return ssl_cert_select_current(s->cert, (X509 *)parg); + + case SSL_CTRL_GET_CURVES: { + const uint16_t *clist = s->s3->tmp.peer_ellipticcurvelist; + size_t clistlen = s->s3->tmp.peer_ellipticcurvelist_length; + if (parg) { + size_t i; + int *cptr = parg; + int nid; + for (i = 0; i < clistlen; i++) { + nid = tls1_ec_curve_id2nid(clist[i]); + if (nid != NID_undef) { + cptr[i] = nid; + } else { + cptr[i] = TLSEXT_nid_unknown | clist[i]; + } + } + } + return (int)clistlen; + } + + case SSL_CTRL_SET_CURVES: + return tls1_set_curves(&s->tlsext_ellipticcurvelist, + &s->tlsext_ellipticcurvelist_length, parg, larg); + + case SSL_CTRL_SET_ECDH_AUTO: + s->cert->ecdh_tmp_auto = larg; + return 1; + + case SSL_CTRL_SET_SIGALGS: + return tls1_set_sigalgs(s->cert, parg, larg, 0); + + case SSL_CTRL_SET_CLIENT_SIGALGS: + return tls1_set_sigalgs(s->cert, parg, larg, 1); + + case SSL_CTRL_GET_CLIENT_CERT_TYPES: { + const uint8_t **pctype = parg; + if (s->server || !s->s3->tmp.cert_req) { + return 0; + } + if (pctype) { + *pctype = s->s3->tmp.certificate_types; + } + return (int)s->s3->tmp.num_certificate_types; + } + + case SSL_CTRL_SET_CLIENT_CERT_TYPES: + if (!s->server) { + return 0; + } + return ssl3_set_req_cert_type(s->cert, parg, larg); + + case SSL_CTRL_BUILD_CERT_CHAIN: + return ssl_build_cert_chain(s->cert, s->ctx->cert_store, larg); + + case SSL_CTRL_SET_VERIFY_CERT_STORE: + return ssl_cert_set_cert_store(s->cert, parg, 0, larg); + + case SSL_CTRL_SET_CHAIN_CERT_STORE: + return ssl_cert_set_cert_store(s->cert, parg, 1, larg); + + case SSL_CTRL_GET_SERVER_TMP_KEY: + if (s->server || !s->session || !s->session->sess_cert) { + return 0; + } else { + SESS_CERT *sc; + EVP_PKEY *ptmp; + int rv = 0; + sc = s->session->sess_cert; + if (!sc->peer_dh_tmp && !sc->peer_ecdh_tmp) { + return 0; + } + ptmp = EVP_PKEY_new(); + if (!ptmp) { + return 0; + } + if (sc->peer_dh_tmp) { + rv = EVP_PKEY_set1_DH(ptmp, sc->peer_dh_tmp); + } else if (sc->peer_ecdh_tmp) { + rv = EVP_PKEY_set1_EC_KEY(ptmp, sc->peer_ecdh_tmp); + } + if (rv) { + *(EVP_PKEY **)parg = ptmp; + return 1; + } + EVP_PKEY_free(ptmp); + return 0; + } + + case SSL_CTRL_GET_EC_POINT_FORMATS: { + const uint8_t **pformat = parg; + if (!s->s3->tmp.peer_ecpointformatlist) { + return 0; + } + *pformat = s->s3->tmp.peer_ecpointformatlist; + return (int)s->s3->tmp.peer_ecpointformatlist_length; + } + + case SSL_CTRL_CHANNEL_ID: + s->tlsext_channel_id_enabled = 1; + ret = 1; + break; + + case SSL_CTRL_SET_CHANNEL_ID: + s->tlsext_channel_id_enabled = 1; + if (EVP_PKEY_bits(parg) != 256) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctrl, SSL_R_CHANNEL_ID_NOT_P256); + break; + } + if (s->tlsext_channel_id_private) { + EVP_PKEY_free(s->tlsext_channel_id_private); + } + s->tlsext_channel_id_private = EVP_PKEY_dup((EVP_PKEY *)parg); + ret = 1; + break; + + case SSL_CTRL_GET_CHANNEL_ID: + if (!s->s3->tlsext_channel_id_valid) { + break; + } + memcpy(parg, s->s3->tlsext_channel_id, larg < 64 ? larg : 64); + return 64; + + case SSL_CTRL_FALLBACK_SCSV: + s->fallback_scsv = 1; + ret = 1; + break; + + default: + break; + } + + return ret; +} + +long ssl3_callback_ctrl(SSL *s, int cmd, void (*fp)(void)) { + int ret = 0; + + if ((cmd == SSL_CTRL_SET_TMP_RSA_CB || cmd == SSL_CTRL_SET_TMP_DH_CB) && + !ssl_cert_inst(&s->cert)) { + OPENSSL_PUT_ERROR(SSL, ssl3_callback_ctrl, ERR_R_MALLOC_FAILURE); + return 0; + } + + switch (cmd) { + case SSL_CTRL_SET_TMP_RSA_CB: + /* Ignore the callback; temporary RSA keys are never used. */ + break; + + case SSL_CTRL_SET_TMP_DH_CB: + s->cert->dh_tmp_cb = (DH * (*)(SSL *, int, int))fp; + break; + + case SSL_CTRL_SET_TMP_ECDH_CB: + s->cert->ecdh_tmp_cb = (EC_KEY * (*)(SSL *, int, int))fp; + break; + + case SSL_CTRL_SET_TLSEXT_DEBUG_CB: + s->tlsext_debug_cb = + (void (*)(SSL *, int, int, uint8_t *, int, void *))fp; + break; + + default: + break; + } + + return ret; +} + +long ssl3_ctx_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) { + CERT *cert; + + cert = ctx->cert; + + switch (cmd) { + case SSL_CTRL_NEED_TMP_RSA: + /* Temporary RSA keys are never used. */ + return 0; + + case SSL_CTRL_SET_TMP_RSA: + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + + case SSL_CTRL_SET_TMP_RSA_CB: + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + + case SSL_CTRL_SET_TMP_DH: { + DH *new = NULL, *dh; + + dh = (DH *)parg; + new = DHparams_dup(dh); + if (new == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_DH_LIB); + return 0; + } + if (!(ctx->options & SSL_OP_SINGLE_DH_USE) && !DH_generate_key(new)) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_DH_LIB); + DH_free(new); + return 0; + } + if (cert->dh_tmp != NULL) { + DH_free(cert->dh_tmp); + } + cert->dh_tmp = new; + return 1; + } + + case SSL_CTRL_SET_TMP_DH_CB: + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + + case SSL_CTRL_SET_TMP_ECDH: { + EC_KEY *ecdh = NULL; + + if (parg == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_ECDH_LIB); + return 0; + } + ecdh = EC_KEY_dup((EC_KEY *)parg); + if (ecdh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_EC_LIB); + return 0; + } + if (!(ctx->options & SSL_OP_SINGLE_ECDH_USE) && + !EC_KEY_generate_key(ecdh)) { + EC_KEY_free(ecdh); + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_ECDH_LIB); + return 0; + } + + if (cert->ecdh_tmp != NULL) { + EC_KEY_free(cert->ecdh_tmp); + } + cert->ecdh_tmp = ecdh; + return 1; + } + + case SSL_CTRL_SET_TMP_ECDH_CB: + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + + case SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG: + ctx->tlsext_servername_arg = parg; + break; + + case SSL_CTRL_SET_TLSEXT_TICKET_KEYS: + case SSL_CTRL_GET_TLSEXT_TICKET_KEYS: { + uint8_t *keys = parg; + if (!keys) { + return 48; + } + if (larg != 48) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, SSL_R_INVALID_TICKET_KEYS_LENGTH); + return 0; + } + if (cmd == SSL_CTRL_SET_TLSEXT_TICKET_KEYS) { + memcpy(ctx->tlsext_tick_key_name, keys, 16); + memcpy(ctx->tlsext_tick_hmac_key, keys + 16, 16); + memcpy(ctx->tlsext_tick_aes_key, keys + 32, 16); + } else { + memcpy(keys, ctx->tlsext_tick_key_name, 16); + memcpy(keys + 16, ctx->tlsext_tick_hmac_key, 16); + memcpy(keys + 32, ctx->tlsext_tick_aes_key, 16); + } + return 1; + } + + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB_ARG: + ctx->tlsext_status_arg = parg; + return 1; + break; + + case SSL_CTRL_SET_CURVES: + return tls1_set_curves(&ctx->tlsext_ellipticcurvelist, + &ctx->tlsext_ellipticcurvelist_length, parg, larg); + + case SSL_CTRL_SET_ECDH_AUTO: + ctx->cert->ecdh_tmp_auto = larg; + return 1; + + case SSL_CTRL_SET_SIGALGS: + return tls1_set_sigalgs(ctx->cert, parg, larg, 0); + + case SSL_CTRL_SET_CLIENT_SIGALGS: + return tls1_set_sigalgs(ctx->cert, parg, larg, 1); + + case SSL_CTRL_SET_CLIENT_CERT_TYPES: + return ssl3_set_req_cert_type(ctx->cert, parg, larg); + + case SSL_CTRL_BUILD_CERT_CHAIN: + return ssl_build_cert_chain(ctx->cert, ctx->cert_store, larg); + + case SSL_CTRL_SET_VERIFY_CERT_STORE: + return ssl_cert_set_cert_store(ctx->cert, parg, 0, larg); + + case SSL_CTRL_SET_CHAIN_CERT_STORE: + return ssl_cert_set_cert_store(ctx->cert, parg, 1, larg); + + case SSL_CTRL_EXTRA_CHAIN_CERT: + if (ctx->extra_certs == NULL) { + ctx->extra_certs = sk_X509_new_null(); + if (ctx->extra_certs == NULL) { + return 0; + } + } + sk_X509_push(ctx->extra_certs, (X509 *)parg); + break; + + case SSL_CTRL_GET_EXTRA_CHAIN_CERTS: + if (ctx->extra_certs == NULL && larg == 0) { + *(STACK_OF(X509) **)parg = ctx->cert->key->chain; + } else { + *(STACK_OF(X509) **)parg = ctx->extra_certs; + } + break; + + case SSL_CTRL_CLEAR_EXTRA_CHAIN_CERTS: + if (ctx->extra_certs) { + sk_X509_pop_free(ctx->extra_certs, X509_free); + ctx->extra_certs = NULL; + } + break; + + case SSL_CTRL_CHAIN: + if (larg) { + return ssl_cert_set1_chain(ctx->cert, (STACK_OF(X509) *)parg); + } else { + return ssl_cert_set0_chain(ctx->cert, (STACK_OF(X509) *)parg); + } + + case SSL_CTRL_CHAIN_CERT: + if (larg) { + return ssl_cert_add1_chain_cert(ctx->cert, (X509 *)parg); + } else { + return ssl_cert_add0_chain_cert(ctx->cert, (X509 *)parg); + } + + case SSL_CTRL_GET_CHAIN_CERTS: + *(STACK_OF(X509) **)parg = ctx->cert->key->chain; + break; + + case SSL_CTRL_SELECT_CURRENT_CERT: + return ssl_cert_select_current(ctx->cert, (X509 *)parg); + + case SSL_CTRL_CHANNEL_ID: + ctx->tlsext_channel_id_enabled = 1; + return 1; + + case SSL_CTRL_SET_CHANNEL_ID: + ctx->tlsext_channel_id_enabled = 1; + if (EVP_PKEY_bits(parg) != 256) { + OPENSSL_PUT_ERROR(SSL, ssl3_ctx_ctrl, SSL_R_CHANNEL_ID_NOT_P256); + break; + } + if (ctx->tlsext_channel_id_private) { + EVP_PKEY_free(ctx->tlsext_channel_id_private); + } + ctx->tlsext_channel_id_private = EVP_PKEY_dup((EVP_PKEY *)parg); + break; + + default: + return 0; + } + + return 1; +} + +long ssl3_ctx_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp)(void)) { + CERT *cert; + + cert = ctx->cert; + + switch (cmd) { + case SSL_CTRL_SET_TMP_RSA_CB: + /* Ignore the callback; temporary RSA keys are never used. */ + break; + + case SSL_CTRL_SET_TMP_DH_CB: + cert->dh_tmp_cb = (DH * (*)(SSL *, int, int))fp; + break; + + case SSL_CTRL_SET_TMP_ECDH_CB: + cert->ecdh_tmp_cb = (EC_KEY * (*)(SSL *, int, int))fp; + break; + + case SSL_CTRL_SET_TLSEXT_SERVERNAME_CB: + ctx->tlsext_servername_callback = (int (*)(SSL *, int *, void *))fp; + break; + + case SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB: + ctx->tlsext_status_cb = (int (*)(SSL *, void *))fp; + break; + + case SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB: + ctx->tlsext_ticket_key_cb = (int ( + *)(SSL *, uint8_t *, uint8_t *, EVP_CIPHER_CTX *, HMAC_CTX *, int))fp; + break; + + default: + return 0; + } + + return 1; +} + +/* ssl3_get_cipher_by_value returns the SSL_CIPHER with value |value| or NULL + * if none exists. + * + * This function needs to check if the ciphers required are actually + * available. */ +const SSL_CIPHER *ssl3_get_cipher_by_value(uint16_t value) { + SSL_CIPHER c; + + c.id = 0x03000000L | value; + return bsearch(&c, ssl3_ciphers, SSL3_NUM_CIPHERS, sizeof(SSL_CIPHER), + ssl_cipher_id_cmp); +} + +/* ssl3_get_cipher_by_value returns the cipher value of |c|. */ +uint16_t ssl3_get_cipher_value(const SSL_CIPHER *c) { + unsigned long id = c->id; + /* All ciphers are SSLv3 now. */ + assert((id & 0xff000000) == 0x03000000); + return id & 0xffff; +} + +struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *s) { + if (s->cipher_list != NULL) { + return s->cipher_list; + } + + if (s->version >= TLS1_1_VERSION && s->ctx != NULL && + s->ctx->cipher_list_tls11 != NULL) { + return s->ctx->cipher_list_tls11; + } + + if (s->ctx != NULL && s->ctx->cipher_list != NULL) { + return s->ctx->cipher_list; + } + + return NULL; +} + +const SSL_CIPHER *ssl3_choose_cipher( + SSL *s, STACK_OF(SSL_CIPHER) * clnt, + struct ssl_cipher_preference_list_st *server_pref) { + const SSL_CIPHER *c, *ret = NULL; + STACK_OF(SSL_CIPHER) *srvr = server_pref->ciphers, *prio, *allow; + size_t i; + int ok; + size_t cipher_index; + unsigned long 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; + + if (s->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(s, &mask_k, &mask_a); + + for (i = 0; i < sk_SSL_CIPHER_num(prio); i++) { + c = sk_SSL_CIPHER_value(prio, i); + + ok = 1; + + /* Skip TLS v1.2 only ciphersuites if not supported */ + if ((c->algorithm_ssl & SSL_TLSV1_2) && !SSL_USE_TLS1_2_CIPHERS(s)) { + 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; + } + } + + return ret; +} + +int ssl3_get_req_cert_type(SSL *s, uint8_t *p) { + int ret = 0; + const uint8_t *sig; + size_t i, siglen; + int have_rsa_sign = 0; + int have_ecdsa_sign = 0; + + /* If we have custom certificate types set, use them */ + if (s->cert->client_certificate_types) { + memcpy(p, s->cert->client_certificate_types, + s->cert->num_client_certificate_types); + return s->cert->num_client_certificate_types; + } + + /* get configured sigalgs */ + siglen = tls12_get_psigalgs(s, &sig); + for (i = 0; i < siglen; i += 2, sig += 2) { + switch (sig[1]) { + case TLSEXT_signature_rsa: + have_rsa_sign = 1; + break; + + case TLSEXT_signature_ecdsa: + have_ecdsa_sign = 1; + break; + } + } + + if (have_rsa_sign) { + p[ret++] = SSL3_CT_RSA_SIGN; + } + + /* ECDSA certs can be used with RSA cipher suites as well so we don't need to + * check for SSL_kECDH or SSL_kEECDH. */ + if (s->version >= TLS1_VERSION && have_ecdsa_sign) { + p[ret++] = TLS_CT_ECDSA_SIGN; + } + + return ret; +} + +static int ssl3_set_req_cert_type(CERT *c, const uint8_t *p, size_t len) { + if (c->client_certificate_types) { + OPENSSL_free(c->client_certificate_types); + c->client_certificate_types = NULL; + } + + c->num_client_certificate_types = 0; + if (!p || !len) { + return 1; + } + + if (len > 0xff) { + return 0; + } + + c->client_certificate_types = BUF_memdup(p, len); + if (!c->client_certificate_types) { + return 0; + } + + c->num_client_certificate_types = len; + return 1; +} + +int ssl3_shutdown(SSL *s) { + int ret; + + /* Do nothing if configured not to send a close_notify. */ + if (s->quiet_shutdown) { + s->shutdown = SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN; + return 1; + } + + if (!(s->shutdown & SSL_SENT_SHUTDOWN)) { + s->shutdown |= SSL_SENT_SHUTDOWN; + ssl3_send_alert(s, SSL3_AL_WARNING, SSL_AD_CLOSE_NOTIFY); + + /* our shutdown alert has been sent now, and if it still needs to be + * written, s->s3->alert_dispatch will be true */ + if (s->s3->alert_dispatch) { + return -1; /* return WANT_WRITE */ + } + } else if (s->s3->alert_dispatch) { + /* resend it if not sent */ + ret = s->method->ssl_dispatch_alert(s); + if (ret == -1) { + /* we only get to return -1 here the 2nd/Nth invocation, we must have + * already signalled return 0 upon a previous invoation, return + * WANT_WRITE */ + return ret; + } + } else if (!(s->shutdown & SSL_RECEIVED_SHUTDOWN)) { + /* If we are waiting for a close from our peer, we are closed */ + s->method->ssl_read_bytes(s, 0, NULL, 0, 0); + if (!(s->shutdown & SSL_RECEIVED_SHUTDOWN)) { + return -1; /* return WANT_READ */ + } + } + + if (s->shutdown == (SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN) && + !s->s3->alert_dispatch) { + return 1; + } else { + return 0; + } +} + +int ssl3_write(SSL *s, const void *buf, int len) { + ERR_clear_system_error(); + if (s->s3->renegotiate) { + ssl3_renegotiate_check(s); + } + + return s->method->ssl_write_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len); +} + +static int ssl3_read_internal(SSL *s, void *buf, int len, int peek) { + int ret; + + ERR_clear_system_error(); + if (s->s3->renegotiate) { + ssl3_renegotiate_check(s); + } + s->s3->in_read_app_data = 1; + ret = s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len, peek); + if (ret == -1 && s->s3->in_read_app_data == 2) { + /* ssl3_read_bytes decided to call s->handshake_func, which called + * ssl3_read_bytes to read handshake data. However, ssl3_read_bytes + * actually found application data and thinks that application data makes + * sense here; so disable handshake processing and try to read application + * data again. */ + s->in_handshake++; + ret = + s->method->ssl_read_bytes(s, SSL3_RT_APPLICATION_DATA, buf, len, peek); + s->in_handshake--; + } else { + s->s3->in_read_app_data = 0; + } + + return ret; +} + +int ssl3_read(SSL *s, void *buf, int len) { + return ssl3_read_internal(s, buf, len, 0); +} + +int ssl3_peek(SSL *s, void *buf, int len) { + return ssl3_read_internal(s, buf, len, 1); +} + +int ssl3_renegotiate(SSL *s) { + if (s->handshake_func == NULL) { + return 1; + } + + s->s3->renegotiate = 1; + return 1; +} + +int ssl3_renegotiate_check(SSL *s) { + if (s->s3->renegotiate && s->s3->rbuf.left == 0 && s->s3->wbuf.left == 0 && + !SSL_in_init(s)) { + /* if we are the server, and we have sent a 'RENEGOTIATE' message, we + * need to go to SSL_ST_ACCEPT. */ + s->state = SSL_ST_RENEGOTIATE; + s->s3->renegotiate = 0; + s->s3->num_renegotiations++; + s->s3->total_renegotiations++; + return 1; + } + + return 0; +} + +/* If we are using default SHA1+MD5 algorithms switch to new SHA256 PRF and + * handshake macs if required. */ +long ssl_get_algorithm2(SSL *s) { + static const unsigned long kMask = SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF; + long alg2 = s->s3->tmp.new_cipher->algorithm2; + if (s->enc_method->enc_flags & SSL_ENC_FLAG_SHA256_PRF && + (alg2 & kMask) == kMask) { + return SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256; + } + return alg2; +} diff --git a/src/ssl/s3_meth.c b/src/ssl/s3_meth.c new file mode 100644 index 0000000..5a25d7b --- /dev/null +++ b/src/ssl/s3_meth.c @@ -0,0 +1,171 @@ +/* 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 "ssl_locl.h" + + +static const SSL_PROTOCOL_METHOD TLS_protocol_method = { + ssl3_new, + ssl3_free, + ssl3_accept, + ssl3_connect, + ssl3_read, + ssl3_peek, + ssl3_write, + ssl3_shutdown, + ssl3_renegotiate, + ssl3_renegotiate_check, + ssl3_get_message, + ssl3_read_bytes, + ssl3_write_bytes, + ssl3_dispatch_alert, + ssl3_ctrl, + ssl3_ctx_ctrl, + ssl3_pending, + ssl3_num_ciphers, + ssl3_get_cipher, + ssl_undefined_void_function, + ssl3_callback_ctrl, + ssl3_ctx_callback_ctrl, +}; + +const SSL_METHOD *TLS_method(void) { + static const SSL_METHOD method = { + 0, + &TLS_protocol_method, + }; + return &method; +} + +const SSL_METHOD *SSLv23_method(void) { + return TLS_method(); +} + +/* Legacy version-locked methods. */ + +const SSL_METHOD *TLSv1_2_method(void) { + static const SSL_METHOD method = { + TLS1_2_VERSION, + &TLS_protocol_method, + }; + return &method; +} + +const SSL_METHOD *TLSv1_1_method(void) { + static const SSL_METHOD method = { + TLS1_1_VERSION, + &TLS_protocol_method, + }; + return &method; +} + +const SSL_METHOD *TLSv1_method(void) { + static const SSL_METHOD method = { + TLS1_VERSION, + &TLS_protocol_method, + }; + return &method; +} + +const SSL_METHOD *SSLv3_method(void) { + static const SSL_METHOD method = { + SSL3_VERSION, + &TLS_protocol_method, + }; + return &method; +} + +/* 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(); +} diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c new file mode 100644 index 0000000..4263cb0 --- /dev/null +++ b/src/ssl/s3_pkt.c @@ -0,0 +1,1216 @@ +/* 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 <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> + +#include <openssl/buf.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/rand.h> + +#include "ssl_locl.h" + + +static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned int len, + char fragment, char is_fragment); +static int ssl3_get_record(SSL *s); + +int ssl3_read_n(SSL *s, int n, int max, int extend) { + /* If |extend| is 0, obtain new n-byte packet; + * if |extend| is 1, increase packet by another n bytes. + * + * The packet will be in the sub-array of |s->s3->rbuf.buf| specified by + * |s->packet| and |s->packet_length|. (If |s->read_ahead| is set, |max| + * bytes may be stored in |rbuf| (plus |s->packet_length| bytes if |extend| + * is one.) */ + int i, len, left; + long align = 0; + uint8_t *pkt; + SSL3_BUFFER *rb; + + if (n <= 0) { + return n; + } + + rb = &s->s3->rbuf; + if (rb->buf == NULL && !ssl3_setup_read_buffer(s)) { + return -1; + } + + left = rb->left; + + align = (long)rb->buf + SSL3_RT_HEADER_LENGTH; + align = (-align) & (SSL3_ALIGN_PAYLOAD - 1); + + if (!extend) { + /* start with empty packet ... */ + if (left == 0) { + rb->offset = align; + } else if (align != 0 && left >= SSL3_RT_HEADER_LENGTH) { + /* check if next packet length is large enough to justify payload + * alignment... */ + pkt = rb->buf + rb->offset; + if (pkt[0] == SSL3_RT_APPLICATION_DATA && (pkt[3] << 8 | pkt[4]) >= 128) { + /* Note that even if packet is corrupted and its length field is + * insane, we can only be led to wrong decision about whether memmove + * will occur or not. Header values has no effect on memmove arguments + * and therefore no buffer overrun can be triggered. */ + memmove(rb->buf + align, pkt, left); + rb->offset = align; + } + } + s->packet = rb->buf + rb->offset; + s->packet_length = 0; + /* ... now we can act as if 'extend' was set */ + } + + /* For DTLS/UDP reads should not span multiple packets because the read + * operation returns the whole packet at once (as long as it fits into the + * buffer). */ + if (SSL_IS_DTLS(s) && left > 0 && n > left) { + n = left; + } + + /* if there is enough in the buffer from a previous read, take some */ + if (left >= n) { + s->packet_length += n; + rb->left = left - n; + rb->offset += n; + return n; + } + + /* else we need to read more data */ + + len = s->packet_length; + pkt = rb->buf + align; + /* Move any available bytes to front of buffer: |len| bytes already pointed + * to by |packet|, |left| extra ones at the end. */ + if (s->packet != pkt) { + /* len > 0 */ + memmove(pkt, s->packet, len + left); + s->packet = pkt; + rb->offset = len + align; + } + + assert(n <= (int)(rb->len - rb->offset)); + + if (!s->read_ahead) { + /* ignore max parameter */ + max = n; + } else { + if (max < n) { + max = n; + } + if (max > (int)(rb->len - rb->offset)) { + max = rb->len - rb->offset; + } + } + + while (left < n) { + /* Now we have len+left bytes at the front of s->s3->rbuf.buf and need to + * read in more until we have len+n (up to len+max if possible). */ + ERR_clear_system_error(); + if (s->rbio != NULL) { + s->rwstate = SSL_READING; + i = BIO_read(s->rbio, pkt + len + left, max - left); + } else { + OPENSSL_PUT_ERROR(SSL, ssl3_read_n, SSL_R_READ_BIO_NOT_SET); + i = -1; + } + + if (i <= 0) { + rb->left = left; + if (s->mode & SSL_MODE_RELEASE_BUFFERS && !SSL_IS_DTLS(s) && + len + left == 0) { + ssl3_release_read_buffer(s); + } + return i; + } + left += i; + /* reads should *never* span multiple packets for DTLS because the + * underlying transport protocol is message oriented as opposed to byte + * oriented as in the TLS case. */ + if (SSL_IS_DTLS(s) && n > left) { + n = left; /* makes the while condition false */ + } + } + + /* done reading, now the book-keeping */ + rb->offset += n; + rb->left = left - n; + s->packet_length += n; + s->rwstate = SSL_NOTHING; + + return n; +} + +/* MAX_EMPTY_RECORDS defines the number of consecutive, empty records that will + * be processed per call to ssl3_get_record. Without this limit an attacker + * could send empty records at a faster rate than we can process and cause + * ssl3_get_record to loop forever. */ +#define MAX_EMPTY_RECORDS 32 + +/* Call this to get a new input record. It will return <= 0 if more data is + * needed, normally due to an error or non-blocking IO. When it finishes, one + * packet has been decoded and can be found in + * ssl->s3->rrec.type - is the type of record + * ssl->s3->rrec.data - data + * ssl->s3->rrec.length - number of bytes */ +/* used only by ssl3_read_bytes */ +static int ssl3_get_record(SSL *s) { + int ssl_major, ssl_minor, al; + int n, i, ret = -1; + SSL3_RECORD *rr; + uint8_t *p; + short version; + size_t extra; + unsigned empty_record_count = 0; + + rr = &s->s3->rrec; + + if (s->options & SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER) { + extra = SSL3_RT_MAX_EXTRA; + } else { + extra = 0; + } + + if (extra && !s->s3->init_extra) { + /* An application error: SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER set after + * ssl3_setup_buffers() was done */ + OPENSSL_PUT_ERROR(SSL, ssl3_get_record, ERR_R_INTERNAL_ERROR); + return -1; + } + +again: + /* check if we have the header */ + if (s->rstate != SSL_ST_READ_BODY || + s->packet_length < SSL3_RT_HEADER_LENGTH) { + n = ssl3_read_n(s, SSL3_RT_HEADER_LENGTH, s->s3->rbuf.len, 0); + if (n <= 0) { + return n; /* error or non-blocking */ + } + s->rstate = SSL_ST_READ_BODY; + + p = s->packet; + if (s->msg_callback) { + s->msg_callback(0, 0, SSL3_RT_HEADER, p, 5, s, s->msg_callback_arg); + } + + /* Pull apart the header into the SSL3_RECORD */ + rr->type = *(p++); + ssl_major = *(p++); + ssl_minor = *(p++); + version = (ssl_major << 8) | ssl_minor; + n2s(p, rr->length); + + if (s->s3->have_version && version != s->version) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_WRONG_VERSION_NUMBER); + if ((s->version & 0xFF00) == (version & 0xFF00)) { + /* Send back error using their minor version number. */ + s->version = (unsigned short)version; + } + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + + if ((version >> 8) != SSL3_VERSION_MAJOR) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_WRONG_VERSION_NUMBER); + goto err; + } + + if (rr->length > s->s3->rbuf.len - SSL3_RT_HEADER_LENGTH) { + al = SSL_AD_RECORD_OVERFLOW; + OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_PACKET_LENGTH_TOO_LONG); + goto f_err; + } + + /* now s->rstate == SSL_ST_READ_BODY */ + } + + /* s->rstate == SSL_ST_READ_BODY, get and decode the data */ + + if (rr->length > s->packet_length - SSL3_RT_HEADER_LENGTH) { + /* now s->packet_length == SSL3_RT_HEADER_LENGTH */ + i = rr->length; + n = ssl3_read_n(s, i, i, 1); + if (n <= 0) { + /* Error or non-blocking IO. Now |n| == |rr->length|, and + * |s->packet_length| == |SSL3_RT_HEADER_LENGTH| + |rr->length|. */ + return n; + } + } + + s->rstate = SSL_ST_READ_HEADER; /* set state for later operations */ + + /* At this point, s->packet_length == SSL3_RT_HEADER_LNGTH + rr->length, and + * we have that many bytes in s->packet. */ + rr->input = &s->packet[SSL3_RT_HEADER_LENGTH]; + + /* ok, we can now read from |s->packet| data into |rr|. |rr->input| points at + * |rr->length| bytes, which need to be copied into |rr->data| by decryption. + * When the data is 'copied' into the |rr->data| buffer, |rr->input| will be + * pointed at the new buffer. */ + + /* We now have - encrypted [ MAC [ compressed [ plain ] ] ] + * rr->length bytes of encrypted compressed stuff. */ + + /* check is not needed I believe */ + if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH + extra) { + al = SSL_AD_RECORD_OVERFLOW; + OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_ENCRYPTED_LENGTH_TOO_LONG); + goto f_err; + } + + /* decrypt in place in 'rr->input' */ + rr->data = rr->input; + + if (!s->enc_method->enc(s, 0)) { + al = SSL_AD_BAD_RECORD_MAC; + OPENSSL_PUT_ERROR(SSL, ssl3_get_record, + SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC); + goto f_err; + } + + if (rr->length > SSL3_RT_MAX_PLAIN_LENGTH + extra) { + al = SSL_AD_RECORD_OVERFLOW; + OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_DATA_LENGTH_TOO_LONG); + goto f_err; + } + + rr->off = 0; + /* So at this point the following is true: + * ssl->s3->rrec.type is the type of record; + * ssl->s3->rrec.length is the number of bytes in the record; + * ssl->s3->rrec.off is the offset to first valid byte; + * ssl->s3->rrec.data is where to take bytes from (increment after use). */ + + /* we have pulled in a full packet so zero things */ + s->packet_length = 0; + + /* just read a 0 length packet */ + if (rr->length == 0) { + empty_record_count++; + if (empty_record_count > MAX_EMPTY_RECORDS) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_record, SSL_R_TOO_MANY_EMPTY_FRAGMENTS); + goto f_err; + } + goto again; + } + + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); +err: + return ret; +} + +/* Call this to write data in records of type |type|. It will return <= 0 if + * not all data has been sent or non-blocking IO. */ +int ssl3_write_bytes(SSL *s, int type, const void *buf_, int len) { + const uint8_t *buf = buf_; + unsigned int tot, n, nw; + int i; + + s->rwstate = SSL_NOTHING; + assert(s->s3->wnum <= INT_MAX); + tot = s->s3->wnum; + s->s3->wnum = 0; + + if (SSL_in_init(s) && !s->in_handshake) { + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_write_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + + /* 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, ssl3_write_bytes, 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 = s->max_send_fragment; + /* fragment is true if do_ssl3_write should send the first byte in its own + * record in order to randomise a CBC IV. */ + int fragment = 0; + + if (n > 1 && s->s3->need_record_splitting && + type == SSL3_RT_APPLICATION_DATA && !s->s3->record_split_done) { + fragment = 1; + /* record_split_done records that the splitting has been done in case we + * hit an SSL_WANT_WRITE condition. In that case, we don't need to do the + * split again. */ + s->s3->record_split_done = 1; + } + + if (n > max) { + nw = max; + } else { + nw = n; + } + + i = do_ssl3_write(s, type, &(buf[tot]), nw, fragment, 0); + if (i <= 0) { + s->s3->wnum = tot; + s->s3->record_split_done = 0; + return i; + } + + if (i == (int)n || (type == SSL3_RT_APPLICATION_DATA && + (s->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) { + /* next chunk of data should get another prepended, one-byte fragment in + * ciphersuites with known-IV weakness. */ + s->s3->record_split_done = 0; + return tot + i; + } + + n -= i; + tot += i; + } +} + +/* do_ssl3_write writes an SSL record of the given type. If |fragment| is 1 + * then it splits the record into a one byte record and a record with the rest + * of the data in order to randomise a CBC IV. If |is_fragment| is true then + * this call resulted from do_ssl3_write calling itself in order to create that + * one byte fragment. */ +static int do_ssl3_write(SSL *s, int type, const uint8_t *buf, unsigned int len, + char fragment, char is_fragment) { + uint8_t *p, *plen; + int i; + int prefix_len = 0; + int eivlen = 0; + long align = 0; + SSL3_RECORD *wr; + SSL3_BUFFER *wb = &(s->s3->wbuf); + + /* first check if there is a SSL3_BUFFER still being written out. This will + * happen with non blocking IO */ + if (wb->left != 0) { + return ssl3_write_pending(s, type, buf, len); + } + + /* If we have an alert to send, lets send it */ + if (s->s3->alert_dispatch) { + i = s->method->ssl_dispatch_alert(s); + if (i <= 0) { + return i; + } + /* if it went, fall through and send more stuff */ + } + + if (wb->buf == NULL && !ssl3_setup_write_buffer(s)) { + return -1; + } + + if (len == 0) { + return 0; + } + + wr = &s->s3->wrec; + + if (fragment) { + /* countermeasure against known-IV weakness in CBC ciphersuites (see + * http://www.openssl.org/~bodo/tls-cbc.txt) */ + prefix_len = do_ssl3_write(s, type, buf, 1 /* length */, 0 /* fragment */, + 1 /* is_fragment */); + if (prefix_len <= 0) { + goto err; + } + + if (prefix_len > + (SSL3_RT_HEADER_LENGTH + SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD)) { + /* insufficient space */ + OPENSSL_PUT_ERROR(SSL, do_ssl3_write, ERR_R_INTERNAL_ERROR); + goto err; + } + } + + if (is_fragment) { + /* The extra fragment would be couple of cipher blocks, and that will be a + * multiple of SSL3_ALIGN_PAYLOAD. So, if we want to align the real + * payload, we can just pretend that we have two headers and a byte. */ + align = (long)wb->buf + 2 * SSL3_RT_HEADER_LENGTH + 1; + align = (-align) & (SSL3_ALIGN_PAYLOAD - 1); + p = wb->buf + align; + wb->offset = align; + } else if (prefix_len) { + p = wb->buf + wb->offset + prefix_len; + } else { + align = (long)wb->buf + SSL3_RT_HEADER_LENGTH; + align = (-align) & (SSL3_ALIGN_PAYLOAD - 1); + p = wb->buf + align; + wb->offset = align; + } + + /* write the header */ + + *(p++) = type & 0xff; + wr->type = type; + + /* Some servers hang if initial ClientHello is larger than 256 bytes and + * record version number > TLS 1.0. */ + if (!s->s3->have_version && s->version > SSL3_VERSION) { + *(p++) = TLS1_VERSION >> 8; + *(p++) = TLS1_VERSION & 0xff; + } else { + *(p++) = s->version >> 8; + *(p++) = s->version & 0xff; + } + + /* field where we are to write out packet length */ + plen = p; + p += 2; + + /* Leave room for the variable nonce for AEADs which specify it explicitly. */ + if (s->aead_write_ctx != NULL && + s->aead_write_ctx->variable_nonce_included_in_record) { + eivlen = s->aead_write_ctx->variable_nonce_len; + } + + /* lets setup the record stuff. */ + wr->data = p + eivlen; + wr->length = (int)(len - (fragment != 0)); + wr->input = (uint8_t *)buf + (fragment != 0); + + /* we now 'read' from wr->input, wr->length bytes into wr->data */ + + memcpy(wr->data, wr->input, wr->length); + wr->input = wr->data; + + /* we should still have the output to wr->data and the input from wr->input. + * Length should be wr->length. wr->data still points in the wb->buf */ + + wr->input = p; + wr->data = p; + wr->length += eivlen; + + if (!s->enc_method->enc(s, 1)) { + goto err; + } + + /* record length after mac and block padding */ + s2n(wr->length, plen); + + if (s->msg_callback) { + s->msg_callback(1, 0, SSL3_RT_HEADER, plen - 5, 5, s, s->msg_callback_arg); + } + + /* we should now have wr->data pointing to the encrypted data, which is + * wr->length long. */ + wr->type = type; /* not needed but helps for debugging */ + wr->length += SSL3_RT_HEADER_LENGTH; + + if (is_fragment) { + /* we are in a recursive call; just return the length, don't write out + * anything. */ + return wr->length; + } + + /* now let's set up wb */ + wb->left = prefix_len + wr->length; + + /* memorize arguments so that ssl3_write_pending can detect bad write retries + * later */ + s->s3->wpend_tot = len; + s->s3->wpend_buf = buf; + s->s3->wpend_type = type; + s->s3->wpend_ret = len; + + /* we now just need to write the buffer */ + return ssl3_write_pending(s, type, buf, len); + +err: + return -1; +} + +/* if s->s3->wbuf.left != 0, we need to call this */ +int ssl3_write_pending(SSL *s, int type, const uint8_t *buf, unsigned int len) { + int i; + SSL3_BUFFER *wb = &(s->s3->wbuf); + + if (s->s3->wpend_tot > (int)len || + (s->s3->wpend_buf != buf && + !(s->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER)) || + s->s3->wpend_type != type) { + OPENSSL_PUT_ERROR(SSL, ssl3_write_pending, SSL_R_BAD_WRITE_RETRY); + return -1; + } + + for (;;) { + ERR_clear_system_error(); + if (s->wbio != NULL) { + s->rwstate = SSL_WRITING; + i = BIO_write(s->wbio, (char *)&(wb->buf[wb->offset]), + (unsigned int)wb->left); + } else { + OPENSSL_PUT_ERROR(SSL, ssl3_write_pending, SSL_R_BIO_NOT_SET); + i = -1; + } + if (i == wb->left) { + wb->left = 0; + wb->offset += i; + if (s->mode & SSL_MODE_RELEASE_BUFFERS && !SSL_IS_DTLS(s)) { + ssl3_release_write_buffer(s); + } + s->rwstate = SSL_NOTHING; + return s->s3->wpend_ret; + } else if (i <= 0) { + if (SSL_IS_DTLS(s)) { + /* For DTLS, just drop it. That's kind of the whole + point in using a datagram service */ + wb->left = 0; + } + return i; + } + wb->offset += i; + wb->left -= i; + } +} + +/* ssl3_expect_change_cipher_spec informs the record layer that a + * ChangeCipherSpec record is required at this point. If a Handshake record is + * received before ChangeCipherSpec, the connection will fail. Moreover, if + * there are unprocessed handshake bytes, the handshake will also fail and the + * function returns zero. Otherwise, the function returns one. */ +int ssl3_expect_change_cipher_spec(SSL *s) { + if (s->s3->handshake_fragment_len > 0 || s->s3->tmp.reuse_message) { + OPENSSL_PUT_ERROR(SSL, ssl3_expect_change_cipher_spec, + SSL_R_UNPROCESSED_HANDSHAKE_DATA); + return 0; + } + + s->s3->flags |= SSL3_FLAGS_EXPECT_CCS; + return 1; +} + +/* Return up to 'len' payload bytes received in 'type' records. + * 'type' is one of the following: + * + * - SSL3_RT_HANDSHAKE (when ssl3_get_message calls us) + * - SSL3_RT_APPLICATION_DATA (when ssl3_read calls us) + * - 0 (during a shutdown, no data has to be returned) + * + * If we don't have stored data to work from, read a SSL/TLS record first + * (possibly multiple records if we still don't have anything to return). + * + * This function must handle any surprises the peer may have for us, such as + * Alert records (e.g. close_notify), ChangeCipherSpec records (not really + * a surprise, but handled as if it were), or renegotiation requests. + * Also if record payloads contain fragments too small to process, we store + * them until there is enough for the respective protocol (the record protocol + * may use arbitrary fragmentation and even interleaving): + * Change cipher spec protocol + * just 1 byte needed, no need for keeping anything stored + * Alert protocol + * 2 bytes needed (AlertLevel, AlertDescription) + * Handshake protocol + * 4 bytes needed (HandshakeType, uint24 length) -- we just have + * to detect unexpected Client Hello and Hello Request messages + * here, anything else is handled by higher layers + * Application data protocol + * none of our business + */ +int ssl3_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek) { + int al, i, j, ret; + unsigned int n; + SSL3_RECORD *rr; + void (*cb)(const SSL *ssl, int type2, int val) = NULL; + uint8_t alert_buffer[2]; + + if (s->s3->rbuf.buf == NULL && !ssl3_setup_read_buffer(s)) { + return -1; + } + + if ((type && type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE) || + (peek && type != SSL3_RT_APPLICATION_DATA)) { + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, ERR_R_INTERNAL_ERROR); + return -1; + } + + if (type == SSL3_RT_HANDSHAKE && s->s3->handshake_fragment_len > 0) { + /* (partially) satisfy request from storage */ + uint8_t *src = s->s3->handshake_fragment; + uint8_t *dst = buf; + unsigned int k; + + /* peek == 0 */ + n = 0; + while (len > 0 && s->s3->handshake_fragment_len > 0) { + *dst++ = *src++; + len--; + s->s3->handshake_fragment_len--; + n++; + } + /* move any remaining fragment bytes: */ + for (k = 0; k < s->s3->handshake_fragment_len; k++) { + s->s3->handshake_fragment[k] = *src++; + } + return n; + } + + /* Now s->s3->handshake_fragment_len == 0 if type == SSL3_RT_HANDSHAKE. */ + + if (!s->in_handshake && SSL_in_init(s)) { + /* type == SSL3_RT_APPLICATION_DATA */ + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + +start: + s->rwstate = SSL_NOTHING; + + /* s->s3->rrec.type - is the type of record + * s->s3->rrec.data - data + * s->s3->rrec.off - offset into 'data' for next read + * s->s3->rrec.length - number of bytes. */ + rr = &s->s3->rrec; + + /* get new packet if necessary */ + if (rr->length == 0 || s->rstate == SSL_ST_READ_BODY) { + ret = ssl3_get_record(s); + if (ret <= 0) { + return ret; + } + } + + /* we now have a packet which can be read and processed */ + + if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec, + * reset by ssl3_get_finished */ + && rr->type != SSL3_RT_HANDSHAKE) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, + SSL_R_DATA_BETWEEN_CCS_AND_FINISHED); + goto f_err; + } + + /* If we are expecting a ChangeCipherSpec, it is illegal to receive a + * Handshake record. */ + if (rr->type == SSL3_RT_HANDSHAKE && (s->s3->flags & SSL3_FLAGS_EXPECT_CCS)) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_HANDSHAKE_RECORD_BEFORE_CCS); + goto f_err; + } + + /* If the other end has shut down, throw anything we read away (even in + * 'peek' mode) */ + if (s->shutdown & SSL_RECEIVED_SHUTDOWN) { + rr->length = 0; + s->rwstate = SSL_NOTHING; + return 0; + } + + if (type == rr->type) { + /* SSL3_RT_APPLICATION_DATA or SSL3_RT_HANDSHAKE */ + /* make sure that we are not getting application data when we are doing a + * handshake for the first time */ + if (SSL_in_init(s) && type == SSL3_RT_APPLICATION_DATA && + s->aead_read_ctx == NULL) { + /* TODO(davidben): Is this check redundant with the handshake_func + * check? */ + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_APP_DATA_IN_HANDSHAKE); + goto f_err; + } + + if (len <= 0) { + return len; + } + + if ((unsigned int)len > rr->length) { + n = rr->length; + } else { + n = (unsigned int)len; + } + + memcpy(buf, &(rr->data[rr->off]), n); + if (!peek) { + rr->length -= n; + rr->off += n; + if (rr->length == 0) { + s->rstate = SSL_ST_READ_HEADER; + rr->off = 0; + if (s->mode & SSL_MODE_RELEASE_BUFFERS && s->s3->rbuf.left == 0) { + ssl3_release_read_buffer(s); + } + } + } + + return n; + } + + + /* If we get here, then type != rr->type; if we have a handshake message, + * then it was unexpected (Hello Request or Client Hello). */ + + /* In case of record types for which we have 'fragment' storage, fill that so + * that we can process the data at a fixed place. */ + + if (rr->type == SSL3_RT_HANDSHAKE) { + const size_t size = sizeof(s->s3->handshake_fragment); + const size_t avail = size - s->s3->handshake_fragment_len; + const size_t todo = (rr->length < avail) ? rr->length : avail; + memcpy(s->s3->handshake_fragment + s->s3->handshake_fragment_len, + &rr->data[rr->off], todo); + rr->off += todo; + rr->length -= todo; + s->s3->handshake_fragment_len += todo; + if (s->s3->handshake_fragment_len < size) { + goto start; /* fragment was too small */ + } + } else if (rr->type == SSL3_RT_ALERT) { + /* Note that this will still allow multiple alerts to be processed in the + * same record */ + if (rr->length < sizeof(alert_buffer)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_ALERT); + goto f_err; + } + memcpy(alert_buffer, &rr->data[rr->off], sizeof(alert_buffer)); + rr->off += sizeof(alert_buffer); + rr->length -= sizeof(alert_buffer); + } + + /* s->s3->handshake_fragment_len == 4 iff rr->type == SSL3_RT_HANDSHAKE; + * (Possibly rr is 'empty' now, i.e. rr->length may be 0.) */ + + /* If we are a client, check for an incoming 'Hello Request': */ + if (!s->server && s->s3->handshake_fragment_len >= 4 && + s->s3->handshake_fragment[0] == SSL3_MT_HELLO_REQUEST && + s->session != NULL && s->session->cipher != NULL) { + s->s3->handshake_fragment_len = 0; + + if (s->s3->handshake_fragment[1] != 0 || + s->s3->handshake_fragment[2] != 0 || + s->s3->handshake_fragment[3] != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_HELLO_REQUEST); + goto f_err; + } + + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, + s->s3->handshake_fragment, 4, s, s->msg_callback_arg); + } + + if (SSL_is_init_finished(s) && !s->s3->renegotiate) { + ssl3_renegotiate(s); + if (ssl3_renegotiate_check(s)) { + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + } + } + /* we either finished a handshake or ignored the request, now try again to + * obtain the (application) data we were asked for */ + goto start; + } + + if (rr->type == SSL3_RT_ALERT) { + const uint8_t alert_level = alert_buffer[0]; + const uint8_t alert_descr = alert_buffer[1]; + + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_ALERT, alert_buffer, 2, s, + s->msg_callback_arg); + } + + if (s->info_callback != NULL) { + cb = s->info_callback; + } else if (s->ctx->info_callback != NULL) { + cb = s->ctx->info_callback; + } + + if (cb != NULL) { + j = (alert_level << 8) | alert_descr; + cb(s, SSL_CB_READ_ALERT, j); + } + + if (alert_level == 1) { + /* warning */ + s->s3->warn_alert = alert_descr; + if (alert_descr == SSL_AD_CLOSE_NOTIFY) { + s->shutdown |= SSL_RECEIVED_SHUTDOWN; + return 0; + } + + /* This is a warning but we receive it if we requested renegotiation and + * the peer denied it. Terminate with a fatal alert because if + * application tried to renegotiatie it presumably had a good reason and + * expects it to succeed. + * + * In future we might have a renegotiation where we don't care if the + * peer refused it where we carry on. */ + else if (alert_descr == SSL_AD_NO_RENEGOTIATION) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_NO_RENEGOTIATION); + goto f_err; + } + } else if (alert_level == 2) { + /* fatal */ + char tmp[16]; + + s->rwstate = SSL_NOTHING; + s->s3->fatal_alert = alert_descr; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, + SSL_AD_REASON_OFFSET + alert_descr); + BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr); + ERR_add_error_data(2, "SSL alert number ", tmp); + s->shutdown |= SSL_RECEIVED_SHUTDOWN; + SSL_CTX_remove_session(s->ctx, s->session); + return 0; + } else { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_UNKNOWN_ALERT_TYPE); + goto f_err; + } + + goto start; + } + + if (s->shutdown & SSL_SENT_SHUTDOWN) { + /* but we have not received a shutdown */ + s->rwstate = SSL_NOTHING; + rr->length = 0; + return 0; + } + + if (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC) { + /* 'Change Cipher Spec' is just a single byte, so we know exactly what the + * record payload has to look like */ + if (rr->length != 1 || rr->off != 0 || rr->data[0] != SSL3_MT_CCS) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_BAD_CHANGE_CIPHER_SPEC); + goto f_err; + } + + /* Check we have a cipher to change to */ + if (s->s3->tmp.new_cipher == NULL) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_CCS_RECEIVED_EARLY); + goto f_err; + } + + if (!(s->s3->flags & SSL3_FLAGS_EXPECT_CCS)) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_CCS_RECEIVED_EARLY); + goto f_err; + } + + s->s3->flags &= ~SSL3_FLAGS_EXPECT_CCS; + + rr->length = 0; + + if (s->msg_callback) { + s->msg_callback(0, s->version, SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, 1, s, + s->msg_callback_arg); + } + + s->s3->change_cipher_spec = 1; + if (!ssl3_do_change_cipher_spec(s)) { + goto err; + } else { + goto start; + } + } + + /* Unexpected handshake message (Client Hello, or protocol violation) */ + if (s->s3->handshake_fragment_len >= 4 && !s->in_handshake) { + if ((s->state & SSL_ST_MASK) == SSL_ST_OK) { + s->state = s->server ? SSL_ST_ACCEPT : SSL_ST_CONNECT; + s->renegotiate = 1; + s->new_session = 1; + } + i = s->handshake_func(s); + if (i < 0) { + return i; + } + if (i == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_SSL_HANDSHAKE_FAILURE); + return -1; + } + + goto start; + } + + switch (rr->type) { + default: + /* TLS up to v1.1 just ignores unknown message types. TLS v1.2 gives an + * unexpected message alert. */ + if (s->version >= TLS1_VERSION && s->version <= TLS1_1_VERSION) { + rr->length = 0; + goto start; + } + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_UNEXPECTED_RECORD); + goto f_err; + + case SSL3_RT_CHANGE_CIPHER_SPEC: + case SSL3_RT_ALERT: + case SSL3_RT_HANDSHAKE: + /* we already handled all of these, with the possible exception of + * SSL3_RT_HANDSHAKE when s->in_handshake is set, but that should not + * happen when type != rr->type */ + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, ERR_R_INTERNAL_ERROR); + goto f_err; + + case SSL3_RT_APPLICATION_DATA: + /* At this point we were expecting handshake data but have application + * data. If the library was running inside ssl3_read() (i.e. + * |in_read_app_data| is set) and it makes sense to read application data + * at this point (session renegotiation not yet started), we will indulge + * it. */ + if (s->s3->in_read_app_data && s->s3->total_renegotiations != 0 && + (((s->state & SSL_ST_CONNECT) && + s->state >= SSL3_ST_CW_CLNT_HELLO_A && + s->state <= SSL3_ST_CR_SRVR_HELLO_A) || + ((s->state & SSL_ST_ACCEPT) && + s->state <= SSL3_ST_SW_HELLO_REQ_A && + s->state >= SSL3_ST_SR_CLNT_HELLO_A))) { + s->s3->in_read_app_data = 2; + return -1; + } else { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_read_bytes, SSL_R_UNEXPECTED_RECORD); + goto f_err; + } + } + /* not reached */ + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); +err: + return -1; +} + +int ssl3_do_change_cipher_spec(SSL *s) { + int i; + + if (s->state & SSL_ST_ACCEPT) { + i = SSL3_CHANGE_CIPHER_SERVER_READ; + } else { + i = SSL3_CHANGE_CIPHER_CLIENT_READ; + } + + if (s->s3->tmp.key_block == NULL) { + if (s->session == NULL || s->session->master_key_length == 0) { + /* might happen if dtls1_read_bytes() calls this */ + OPENSSL_PUT_ERROR(SSL, ssl3_do_change_cipher_spec, + SSL_R_CCS_RECEIVED_EARLY); + return 0; + } + + s->session->cipher = s->s3->tmp.new_cipher; + if (!s->enc_method->setup_key_block(s)) { + return 0; + } + } + + if (!s->enc_method->change_cipher_state(s, i)) { + return 0; + } + + return 1; +} + +int ssl3_send_alert(SSL *s, int level, int desc) { + /* Map tls/ssl alert value to correct one */ + desc = s->enc_method->alert_value(desc); + if (s->version == SSL3_VERSION && desc == SSL_AD_PROTOCOL_VERSION) { + /* SSL 3.0 does not have protocol_version alerts */ + desc = SSL_AD_HANDSHAKE_FAILURE; + } + if (desc < 0) { + return -1; + } + + /* If a fatal one, remove from cache */ + if (level == 2 && s->session != NULL) { + SSL_CTX_remove_session(s->ctx, s->session); + } + + s->s3->alert_dispatch = 1; + s->s3->send_alert[0] = level; + s->s3->send_alert[1] = desc; + if (s->s3->wbuf.left == 0) { + /* data is still being written out. */ + return s->method->ssl_dispatch_alert(s); + } + + /* else data is still being written out, we will get written some time in the + * future */ + return -1; +} + +int ssl3_dispatch_alert(SSL *s) { + int i, j; + void (*cb)(const SSL *ssl, int type, int val) = NULL; + + s->s3->alert_dispatch = 0; + i = do_ssl3_write(s, SSL3_RT_ALERT, &s->s3->send_alert[0], 2, 0, 0); + if (i <= 0) { + s->s3->alert_dispatch = 1; + } else { + /* Alert sent to BIO. If it is important, flush it now. If the message + * does not get sent due to non-blocking IO, we will not worry too much. */ + if (s->s3->send_alert[0] == SSL3_AL_FATAL) { + BIO_flush(s->wbio); + } + + if (s->msg_callback) { + s->msg_callback(1, s->version, SSL3_RT_ALERT, s->s3->send_alert, 2, s, + s->msg_callback_arg); + } + + if (s->info_callback != NULL) { + cb = s->info_callback; + } else if (s->ctx->info_callback != NULL) { + cb = s->ctx->info_callback; + } + + if (cb != NULL) { + j = (s->s3->send_alert[0] << 8) | s->s3->send_alert[1]; + cb(s, SSL_CB_WRITE_ALERT, j); + } + } + + return i; +} diff --git a/src/ssl/s3_srvr.c b/src/ssl/s3_srvr.c new file mode 100644 index 0000000..b346d14 --- /dev/null +++ b/src/ssl/s3_srvr.c @@ -0,0 +1,2807 @@ +/* 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. */ + +#define NETSCAPE_HANG_BUG + +#include <assert.h> +#include <stdio.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/evp.h> +#include <openssl/hmac.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/rand.h> +#include <openssl/sha.h> +#include <openssl/x509.h> + +#include "ssl_locl.h" +#include "../crypto/internal.h" +#include "../crypto/dh/internal.h" + + +/* INITIAL_SNIFF_BUFFER_SIZE is the number of bytes read in the initial sniff + * buffer. */ +#define INITIAL_SNIFF_BUFFER_SIZE 8 + +int ssl3_accept(SSL *s) { + BUF_MEM *buf = NULL; + unsigned long alg_a; + void (*cb)(const SSL *ssl, int type, int val) = NULL; + int ret = -1; + int new_state, state, skip = 0; + + assert(s->handshake_func == ssl3_accept); + assert(s->server); + assert(!SSL_IS_DTLS(s)); + + ERR_clear_error(); + ERR_clear_system_error(); + + if (s->info_callback != NULL) { + cb = s->info_callback; + } else if (s->ctx->info_callback != NULL) { + cb = s->ctx->info_callback; + } + + s->in_handshake++; + + if (s->cert == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_accept, SSL_R_NO_CERTIFICATE_SET); + return -1; + } + + for (;;) { + state = s->state; + + switch (s->state) { + case SSL_ST_RENEGOTIATE: + /* This state is the renegotiate entry point. It sends a HelloRequest + * and nothing else. */ + s->renegotiate = 1; + + if (cb != NULL) { + cb(s, SSL_CB_HANDSHAKE_START, 1); + } + + if (s->init_buf == NULL) { + buf = BUF_MEM_new(); + if (!buf || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { + ret = -1; + goto end; + } + s->init_buf = buf; + buf = NULL; + } + s->init_num = 0; + + if (!ssl3_setup_buffers(s)) { + ret = -1; + goto end; + } + + if (!s->s3->send_connection_binding && + !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) { + /* Server attempting to renegotiate with client that doesn't support + * secure renegotiation. */ + OPENSSL_PUT_ERROR(SSL, ssl3_accept, + SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + ret = -1; + goto end; + } + + s->ctx->stats.sess_accept_renegotiate++; + s->state = SSL3_ST_SW_HELLO_REQ_A; + break; + + case SSL3_ST_SW_HELLO_REQ_A: + case SSL3_ST_SW_HELLO_REQ_B: + s->shutdown = 0; + ret = ssl3_send_hello_request(s); + if (ret <= 0) { + goto end; + } + s->s3->tmp.next_state = SSL3_ST_SW_HELLO_REQ_C; + s->state = SSL3_ST_SW_FLUSH; + s->init_num = 0; + + if (!ssl3_init_finished_mac(s)) { + OPENSSL_PUT_ERROR(SSL, ssl3_accept, ERR_R_INTERNAL_ERROR); + ret = -1; + goto end; + } + break; + + case SSL3_ST_SW_HELLO_REQ_C: + s->state = SSL_ST_OK; + break; + + case SSL_ST_ACCEPT: + case SSL_ST_BEFORE | SSL_ST_ACCEPT: + /* This state is the entry point for the handshake itself (initial and + * renegotiation). */ + if (cb != NULL) { + cb(s, SSL_CB_HANDSHAKE_START, 1); + } + + if (s->init_buf == NULL) { + buf = BUF_MEM_new(); + if (!buf || !BUF_MEM_grow(buf, SSL3_RT_MAX_PLAIN_LENGTH)) { + ret = -1; + goto end; + } + s->init_buf = buf; + buf = NULL; + } + s->init_num = 0; + + if (!ssl3_init_finished_mac(s)) { + OPENSSL_PUT_ERROR(SSL, ssl3_accept, ERR_R_INTERNAL_ERROR); + ret = -1; + goto end; + } + + if (!s->s3->have_version) { + /* This is the initial handshake. The record layer has not been + * initialized yet. Sniff for a V2ClientHello before reading a + * ClientHello normally. */ + assert(s->s3->rbuf.buf == NULL); + assert(s->s3->wbuf.buf == NULL); + s->state = SSL3_ST_SR_INITIAL_BYTES; + } else { + /* Enable a write buffer. This groups handshake messages within a + * flight into a single write. */ + if (!ssl3_setup_buffers(s) || !ssl_init_wbio_buffer(s, 1)) { + ret = -1; + goto end; + } + s->state = SSL3_ST_SR_CLNT_HELLO_A; + } + s->ctx->stats.sess_accept++; + break; + + case SSL3_ST_SR_INITIAL_BYTES: + ret = ssl3_get_initial_bytes(s); + if (ret <= 0) { + goto end; + } + /* ssl3_get_initial_bytes sets s->state to one of + * SSL3_ST_SR_V2_CLIENT_HELLO or SSL3_ST_SR_CLNT_HELLO_A on success. */ + break; + + case SSL3_ST_SR_V2_CLIENT_HELLO: + ret = ssl3_get_v2_client_hello(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SR_CLNT_HELLO_A; + break; + + case SSL3_ST_SR_CLNT_HELLO_A: + case SSL3_ST_SR_CLNT_HELLO_B: + case SSL3_ST_SR_CLNT_HELLO_C: + case SSL3_ST_SR_CLNT_HELLO_D: + s->shutdown = 0; + ret = ssl3_get_client_hello(s); + if (ret == PENDING_SESSION) { + s->rwstate = SSL_PENDING_SESSION; + goto end; + } + if (ret == CERTIFICATE_SELECTION_PENDING) { + s->rwstate = SSL_CERTIFICATE_SELECTION_PENDING; + goto end; + } + if (ret <= 0) { + goto end; + } + s->renegotiate = 2; + s->state = SSL3_ST_SW_SRVR_HELLO_A; + s->init_num = 0; + break; + + case SSL3_ST_SW_SRVR_HELLO_A: + case SSL3_ST_SW_SRVR_HELLO_B: + ret = ssl3_send_server_hello(s); + if (ret <= 0) { + goto end; + } + if (s->hit) { + if (s->tlsext_ticket_expected) { + s->state = SSL3_ST_SW_SESSION_TICKET_A; + } else { + s->state = SSL3_ST_SW_CHANGE_A; + } + } else { + s->state = SSL3_ST_SW_CERT_A; + } + s->init_num = 0; + break; + + case SSL3_ST_SW_CERT_A: + case SSL3_ST_SW_CERT_B: + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + ret = ssl3_send_server_certificate(s); + if (ret <= 0) { + goto end; + } + if (s->s3->tmp.certificate_status_expected) { + s->state = SSL3_ST_SW_CERT_STATUS_A; + } else { + s->state = SSL3_ST_SW_KEY_EXCH_A; + } + } else { + skip = 1; + s->state = SSL3_ST_SW_KEY_EXCH_A; + } + s->init_num = 0; + break; + + case SSL3_ST_SW_KEY_EXCH_A: + case SSL3_ST_SW_KEY_EXCH_B: + alg_a = s->s3->tmp.new_cipher->algorithm_auth; + + /* Send a ServerKeyExchange message if: + * - The key exchange is ephemeral or anonymous + * Diffie-Hellman. + * - There is a PSK identity hint. + * + * TODO(davidben): This logic is currently duplicated in d1_srvr.c. Fix + * this. In the meantime, keep them in sync. */ + if (ssl_cipher_requires_server_key_exchange(s->s3->tmp.new_cipher) || + ((alg_a & SSL_aPSK) && s->psk_identity_hint)) { + ret = ssl3_send_server_key_exchange(s); + if (ret <= 0) + goto end; + } else { + skip = 1; + } + + s->state = SSL3_ST_SW_CERT_REQ_A; + s->init_num = 0; + break; + + case SSL3_ST_SW_CERT_REQ_A: + case SSL3_ST_SW_CERT_REQ_B: + if (/* don't request cert unless asked for it: */ + !(s->verify_mode & SSL_VERIFY_PEER) || + /* Don't request a certificate if an obc was presented */ + ((s->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) && + s->s3->tlsext_channel_id_valid) || + /* if SSL_VERIFY_CLIENT_ONCE is set, + * don't request cert during re-negotiation: */ + ((s->session->peer != NULL) && + (s->verify_mode & SSL_VERIFY_CLIENT_ONCE)) || + /* never request cert in anonymous ciphersuites + * (see section "Certificate request" in SSL 3 drafts + * and in RFC 2246): */ + ((s->s3->tmp.new_cipher->algorithm_auth & SSL_aNULL) && + /* ... except when the application insists on verification + * (against the specs, but s3_clnt.c accepts this for SSL 3) */ + !(s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) || + /* With normal PSK Certificates and + * Certificate Requests are omitted */ + (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) { + /* no cert request */ + skip = 1; + s->s3->tmp.cert_request = 0; + s->state = SSL3_ST_SW_SRVR_DONE_A; + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + return -1; + } + } else { + s->s3->tmp.cert_request = 1; + ret = ssl3_send_certificate_request(s); + if (ret <= 0) { + goto end; + } +#ifndef NETSCAPE_HANG_BUG + s->state = SSL3_ST_SW_SRVR_DONE_A; +#else + /* ServerHelloDone was already sent in the + * previous record. */ + s->state = SSL3_ST_SW_FLUSH; + s->s3->tmp.next_state = SSL3_ST_SR_CERT_A; +#endif + s->init_num = 0; + } + break; + + case SSL3_ST_SW_SRVR_DONE_A: + case SSL3_ST_SW_SRVR_DONE_B: + ret = ssl3_send_server_done(s); + if (ret <= 0) { + goto end; + } + s->s3->tmp.next_state = SSL3_ST_SR_CERT_A; + s->state = SSL3_ST_SW_FLUSH; + s->init_num = 0; + break; + + case SSL3_ST_SW_FLUSH: + /* This code originally checked to see if any data was pending using + * BIO_CTRL_INFO and then flushed. This caused problems as documented + * in PR#1939. The proposed fix doesn't completely resolve this issue + * as buggy implementations of BIO_CTRL_PENDING still exist. So instead + * we just flush unconditionally. */ + s->rwstate = SSL_WRITING; + if (BIO_flush(s->wbio) <= 0) { + ret = -1; + goto end; + } + s->rwstate = SSL_NOTHING; + + s->state = s->s3->tmp.next_state; + break; + + case SSL3_ST_SR_CERT_A: + case SSL3_ST_SR_CERT_B: + if (s->s3->tmp.cert_request) { + ret = ssl3_get_client_certificate(s); + if (ret <= 0) { + goto end; + } + } + s->init_num = 0; + s->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(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SR_CERT_VRFY_A; + s->init_num = 0; + break; + + case SSL3_ST_SR_CERT_VRFY_A: + case SSL3_ST_SR_CERT_VRFY_B: + ret = ssl3_get_cert_verify(s); + if (ret <= 0) { + goto end; + } + + s->state = SSL3_ST_SR_CHANGE; + s->init_num = 0; + break; + + case SSL3_ST_SR_CHANGE: { + char next_proto_neg = 0; + char channel_id = 0; + next_proto_neg = s->s3->next_proto_neg_seen; + channel_id = s->s3->tlsext_channel_id_valid; + + /* At this point, the next message must be entirely behind a + * ChangeCipherSpec. */ + if (!ssl3_expect_change_cipher_spec(s)) { + ret = -1; + goto end; + } + if (next_proto_neg) { + s->state = SSL3_ST_SR_NEXT_PROTO_A; + } else if (channel_id) { + s->state = SSL3_ST_SR_CHANNEL_ID_A; + } else { + s->state = SSL3_ST_SR_FINISHED_A; + } + break; + } + + case SSL3_ST_SR_NEXT_PROTO_A: + case SSL3_ST_SR_NEXT_PROTO_B: + ret = ssl3_get_next_proto(s); + if (ret <= 0) { + goto end; + } + s->init_num = 0; + if (s->s3->tlsext_channel_id_valid) { + s->state = SSL3_ST_SR_CHANNEL_ID_A; + } else { + s->state = SSL3_ST_SR_FINISHED_A; + } + break; + + case SSL3_ST_SR_CHANNEL_ID_A: + case SSL3_ST_SR_CHANNEL_ID_B: + ret = ssl3_get_channel_id(s); + if (ret <= 0) { + goto end; + } + s->init_num = 0; + s->state = SSL3_ST_SR_FINISHED_A; + break; + + case SSL3_ST_SR_FINISHED_A: + case SSL3_ST_SR_FINISHED_B: + ret = + ssl3_get_finished(s, SSL3_ST_SR_FINISHED_A, SSL3_ST_SR_FINISHED_B); + if (ret <= 0) { + goto end; + } + + if (s->hit) { + s->state = SSL_ST_OK; + } else if (s->tlsext_ticket_expected) { + s->state = SSL3_ST_SW_SESSION_TICKET_A; + } else { + s->state = SSL3_ST_SW_CHANGE_A; + } + /* If this is a full handshake with ChannelID then record the hashshake + * hashes in |s->session| in case we need them to verify a ChannelID + * signature on a resumption of this session in the future. */ + if (!s->hit && s->s3->tlsext_channel_id_new) { + ret = tls1_record_handshake_hashes_for_channel_id(s); + if (ret <= 0) { + goto end; + } + } + s->init_num = 0; + break; + + case SSL3_ST_SW_SESSION_TICKET_A: + case SSL3_ST_SW_SESSION_TICKET_B: + ret = ssl3_send_new_session_ticket(s); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SW_CHANGE_A; + s->init_num = 0; + break; + + case SSL3_ST_SW_CHANGE_A: + case SSL3_ST_SW_CHANGE_B: + s->session->cipher = s->s3->tmp.new_cipher; + if (!s->enc_method->setup_key_block(s)) { + ret = -1; + goto end; + } + + ret = ssl3_send_change_cipher_spec(s, SSL3_ST_SW_CHANGE_A, + SSL3_ST_SW_CHANGE_B); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SW_FINISHED_A; + s->init_num = 0; + + if (!s->enc_method->change_cipher_state( + s, SSL3_CHANGE_CIPHER_SERVER_WRITE)) { + ret = -1; + goto end; + } + break; + + case SSL3_ST_SW_FINISHED_A: + case SSL3_ST_SW_FINISHED_B: + ret = + ssl3_send_finished(s, SSL3_ST_SW_FINISHED_A, SSL3_ST_SW_FINISHED_B, + s->enc_method->server_finished_label, + s->enc_method->server_finished_label_len); + if (ret <= 0) { + goto end; + } + s->state = SSL3_ST_SW_FLUSH; + if (s->hit) { + s->s3->tmp.next_state = SSL3_ST_SR_CHANGE; + } else { + s->s3->tmp.next_state = SSL_ST_OK; + } + s->init_num = 0; + break; + + case SSL_ST_OK: + /* clean a few things up */ + ssl3_cleanup_key_block(s); + + BUF_MEM_free(s->init_buf); + s->init_buf = NULL; + + /* remove buffering on output */ + ssl_free_wbio_buffer(s); + + s->init_num = 0; + + /* If we aren't retaining peer certificates then we can discard it + * now. */ + if (s->session->peer && s->ctx->retain_only_sha256_of_client_certs) { + X509_free(s->session->peer); + s->session->peer = NULL; + } + + if (s->renegotiate == 2) { + /* skipped if we just sent a HelloRequest */ + s->renegotiate = 0; + s->new_session = 0; + + ssl_update_cache(s, SSL_SESS_CACHE_SERVER); + + s->ctx->stats.sess_accept_good++; + + if (cb != NULL) { + cb(s, SSL_CB_HANDSHAKE_DONE, 1); + } + } + + ret = 1; + goto end; + + default: + OPENSSL_PUT_ERROR(SSL, ssl3_accept, SSL_R_UNKNOWN_STATE); + ret = -1; + goto end; + } + + if (!s->s3->tmp.reuse_message && !skip && cb != NULL && s->state != state) { + new_state = s->state; + s->state = state; + cb(s, SSL_CB_ACCEPT_LOOP, 1); + s->state = new_state; + } + skip = 0; + } + +end: + s->in_handshake--; + if (buf != NULL) { + BUF_MEM_free(buf); + } + if (cb != NULL) { + cb(s, SSL_CB_ACCEPT_EXIT, ret); + } + return ret; +} + +static int ssl3_read_sniff_buffer(SSL *s, size_t n) { + if (s->s3->sniff_buffer == NULL) { + s->s3->sniff_buffer = BUF_MEM_new(); + } + if (s->s3->sniff_buffer == NULL || !BUF_MEM_grow(s->s3->sniff_buffer, n)) { + return -1; + } + + while (s->s3->sniff_buffer_len < n) { + int ret; + + s->rwstate = SSL_READING; + ret = BIO_read(s->rbio, s->s3->sniff_buffer->data + s->s3->sniff_buffer_len, + n - s->s3->sniff_buffer_len); + if (ret <= 0) { + return ret; + } + s->rwstate = SSL_NOTHING; + s->s3->sniff_buffer_len += ret; + } + + return 1; +} + +int ssl3_get_initial_bytes(SSL *s) { + int ret; + const uint8_t *p; + + /* Read the first 8 bytes. To recognize a ClientHello or V2ClientHello only + * needs the first 6 bytes, but 8 is needed to recognize CONNECT below. */ + ret = ssl3_read_sniff_buffer(s, INITIAL_SNIFF_BUFFER_SIZE); + if (ret <= 0) { + return ret; + } + assert(s->s3->sniff_buffer_len >= INITIAL_SNIFF_BUFFER_SIZE); + p = (const uint8_t *)s->s3->sniff_buffer->data; + + /* 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, ssl3_get_initial_bytes, SSL_R_HTTP_REQUEST); + return -1; + } + if (strncmp("CONNECT ", (const char *)p, 8) == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_initial_bytes, SSL_R_HTTPS_PROXY_REQUEST); + return -1; + } + + /* Determine if this is a ClientHello or V2ClientHello. */ + if ((p[0] & 0x80) && p[2] == SSL2_MT_CLIENT_HELLO && + p[3] >= SSL3_VERSION_MAJOR) { + /* This is a V2ClientHello. */ + s->state = SSL3_ST_SR_V2_CLIENT_HELLO; + return 1; + } + if (p[0] == SSL3_RT_HANDSHAKE && p[1] >= SSL3_VERSION_MAJOR && + p[5] == SSL3_MT_CLIENT_HELLO) { + /* This is a ClientHello. Initialize the record layer with the already + * consumed data and continue the handshake. */ + if (!ssl3_setup_buffers(s) || !ssl_init_wbio_buffer(s, 1)) { + return -1; + } + assert(s->rstate == SSL_ST_READ_HEADER); + memcpy(s->s3->rbuf.buf, p, s->s3->sniff_buffer_len); + s->s3->rbuf.offset = 0; + s->s3->rbuf.left = s->s3->sniff_buffer_len; + s->packet_length = 0; + + BUF_MEM_free(s->s3->sniff_buffer); + s->s3->sniff_buffer = NULL; + s->s3->sniff_buffer_len = 0; + + s->state = SSL3_ST_SR_CLNT_HELLO_A; + return 1; + } + + OPENSSL_PUT_ERROR(SSL, ssl3_get_initial_bytes, SSL_R_UNKNOWN_PROTOCOL); + return -1; +} + +int ssl3_get_v2_client_hello(SSL *s) { + const uint8_t *p; + int ret; + CBS v2_client_hello, cipher_specs, session_id, challenge; + size_t msg_length, rand_len, len; + uint8_t msg_type; + uint16_t version, cipher_spec_length, session_id_length, challenge_length; + CBB client_hello, hello_body, cipher_suites; + uint8_t random[SSL3_RANDOM_SIZE]; + + /* Read the remainder of the V2ClientHello. We have previously read 8 bytes + * in ssl3_get_initial_bytes. */ + assert(s->s3->sniff_buffer_len >= INITIAL_SNIFF_BUFFER_SIZE); + p = (const uint8_t *)s->s3->sniff_buffer->data; + msg_length = ((p[0] & 0x7f) << 8) | p[1]; + if (msg_length > (1024 * 4)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, SSL_R_RECORD_TOO_LARGE); + return -1; + } + if (msg_length < INITIAL_SNIFF_BUFFER_SIZE - 2) { + /* Reject lengths that are too short early. We have already read 8 bytes, + * so we should not attempt to process an (invalid) V2ClientHello which + * would be shorter than that. */ + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, + SSL_R_RECORD_LENGTH_MISMATCH); + return -1; + } + + ret = ssl3_read_sniff_buffer(s, msg_length + 2); + if (ret <= 0) { + return ret; + } + assert(s->s3->sniff_buffer_len == msg_length + 2); + CBS_init(&v2_client_hello, (const uint8_t *)s->s3->sniff_buffer->data + 2, + msg_length); + + /* The V2ClientHello without the length is incorporated into the Finished + * hash. */ + ssl3_finish_mac(s, CBS_data(&v2_client_hello), CBS_len(&v2_client_hello)); + if (s->msg_callback) { + s->msg_callback(0, SSL2_VERSION, 0, CBS_data(&v2_client_hello), + CBS_len(&v2_client_hello), s, s->msg_callback_arg); + } + + 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, ssl3_get_v2_client_hello, 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. */ + memset(random, 0, SSL3_RANDOM_SIZE); + rand_len = CBS_len(&challenge); + if (rand_len > SSL3_RANDOM_SIZE) { + rand_len = SSL3_RANDOM_SIZE; + } + memcpy(random + (SSL3_RANDOM_SIZE - rand_len), CBS_data(&challenge), + rand_len); + + /* Write out an equivalent SSLv3 ClientHello. */ + if (!CBB_init_fixed(&client_hello, (uint8_t *)s->init_buf->data, + s->init_buf->max)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_MALLOC_FAILURE); + return -1; + } + if (!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, ssl3_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + 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, ssl3_get_v2_client_hello, 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, ssl3_get_v2_client_hello, 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, &len)) { + CBB_cleanup(&client_hello); + OPENSSL_PUT_ERROR(SSL, ssl3_get_v2_client_hello, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* Mark the message for "re"-use by the version-specific method. */ + s->s3->tmp.reuse_message = 1; + s->s3->tmp.message_type = SSL3_MT_CLIENT_HELLO; + /* The handshake message header is 4 bytes. */ + s->s3->tmp.message_size = len - 4; + + /* Initialize the record layer. */ + if (!ssl3_setup_buffers(s) || !ssl_init_wbio_buffer(s, 1)) { + return -1; + } + + /* Drop the sniff buffer. */ + BUF_MEM_free(s->s3->sniff_buffer); + s->s3->sniff_buffer = NULL; + s->s3->sniff_buffer_len = 0; + + return 1; +} + +int ssl3_send_hello_request(SSL *s) { + if (s->state == SSL3_ST_SW_HELLO_REQ_A) { + ssl_set_handshake_header(s, SSL3_MT_HELLO_REQUEST, 0); + s->state = SSL3_ST_SW_HELLO_REQ_B; + } + + /* SSL3_ST_SW_HELLO_REQ_B */ + return ssl_do_write(s); +} + +int ssl3_get_client_hello(SSL *s) { + int i, ok, al = SSL_AD_INTERNAL_ERROR, ret = -1; + long n; + const SSL_CIPHER *c; + STACK_OF(SSL_CIPHER) *ciphers = NULL; + struct ssl_early_callback_ctx early_ctx; + CBS client_hello; + uint16_t client_version; + CBS client_random, session_id, cipher_suites, compression_methods; + + /* We do this so that we will respond with our native type. If we are TLSv1 + * and we get SSLv3, we will respond with TLSv1, This down switching should + * be handled by a different method. If we are SSLv3, we will respond with + * SSLv3, even if prompted with TLSv1. */ + switch (s->state) { + case SSL3_ST_SR_CLNT_HELLO_A: + case SSL3_ST_SR_CLNT_HELLO_B: + n = s->method->ssl_get_message( + s, SSL3_ST_SR_CLNT_HELLO_A, SSL3_ST_SR_CLNT_HELLO_B, + SSL3_MT_CLIENT_HELLO, SSL3_RT_MAX_PLAIN_LENGTH, + SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + /* If we require cookies and this ClientHello doesn't contain one, just + * return since we do not want to allocate any memory yet. So check + * cookie length... */ + if (SSL_IS_DTLS(s) && (SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE)) { + uint8_t cookie_length; + + CBS_init(&client_hello, s->init_msg, n); + if (!CBS_skip(&client_hello, 2 + SSL3_RANDOM_SIZE) || + !CBS_get_u8_length_prefixed(&client_hello, &session_id) || + !CBS_get_u8(&client_hello, &cookie_length)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (cookie_length == 0) { + return 1; + } + } + s->state = SSL3_ST_SR_CLNT_HELLO_C; + /* fallthrough */ + case SSL3_ST_SR_CLNT_HELLO_C: + case SSL3_ST_SR_CLNT_HELLO_D: + /* We have previously parsed the ClientHello message, and can't call + * ssl_get_message again without hashing the message into the Finished + * digest again. */ + n = s->init_num; + + memset(&early_ctx, 0, sizeof(early_ctx)); + early_ctx.ssl = s; + early_ctx.client_hello = s->init_msg; + early_ctx.client_hello_len = n; + if (!ssl_early_callback_init(&early_ctx)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, + SSL_R_CLIENTHELLO_PARSE_FAILED); + goto f_err; + } + + if (s->state == SSL3_ST_SR_CLNT_HELLO_C && + s->ctx->select_certificate_cb != NULL) { + s->state = SSL3_ST_SR_CLNT_HELLO_D; + switch (s->ctx->select_certificate_cb(&early_ctx)) { + case 0: + return CERTIFICATE_SELECTION_PENDING; + + case -1: + /* Connection rejected. */ + al = SSL_AD_ACCESS_DENIED; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, + SSL_R_CONNECTION_REJECTED); + goto f_err; + + default: + /* fallthrough */; + } + } + s->state = SSL3_ST_SR_CLNT_HELLO_D; + break; + + default: + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_UNKNOWN_STATE); + return -1; + } + + CBS_init(&client_hello, s->init_msg, n); + if (!CBS_get_u16(&client_hello, &client_version) || + !CBS_get_bytes(&client_hello, &client_random, SSL3_RANDOM_SIZE) || + !CBS_get_u8_length_prefixed(&client_hello, &session_id) || + CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR); + goto f_err; + } + + /* use version from inside client hello, not from record header (may differ: + * see RFC 2246, Appendix E, second paragraph) */ + s->client_version = client_version; + + /* Load the client random. */ + memcpy(s->s3->client_random, CBS_data(&client_random), SSL3_RANDOM_SIZE); + + if (SSL_IS_DTLS(s)) { + CBS cookie; + + if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) || + CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR); + goto f_err; + } + + /* Verify the cookie if appropriate option is set. */ + if ((SSL_get_options(s) & SSL_OP_COOKIE_EXCHANGE) && CBS_len(&cookie) > 0) { + if (s->ctx->app_verify_cookie_cb != NULL) { + if (s->ctx->app_verify_cookie_cb(s, CBS_data(&cookie), + CBS_len(&cookie)) == 0) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_COOKIE_MISMATCH); + goto f_err; + } + /* else cookie verification succeeded */ + } else if (!CBS_mem_equal(&cookie, s->d1->cookie, s->d1->cookie_len)) { + /* default verification */ + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_COOKIE_MISMATCH); + goto f_err; + } + /* Set to -2 so if successful we return 2 and don't send + * HelloVerifyRequest. */ + ret = -2; + } + } + + if (!s->s3->have_version) { + /* Select version to use */ + uint16_t version = ssl3_get_mutual_version(s, client_version); + if (version == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_UNSUPPORTED_PROTOCOL); + s->version = s->client_version; + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + s->version = version; + s->enc_method = ssl3_get_enc_method(version); + assert(s->enc_method != NULL); + /* At this point, the connection's version is known and |s->version| is + * fixed. Begin enforcing the record-layer version. */ + s->s3->have_version = 1; + } else if (SSL_IS_DTLS(s) ? (s->client_version > s->version) + : (s->client_version < s->version)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_WRONG_VERSION_NUMBER); + al = SSL_AD_PROTOCOL_VERSION; + goto f_err; + } + + s->hit = 0; + /* Versions before 0.9.7 always allow clients to resume sessions in + * renegotiation. 0.9.7 and later allow this by default, but optionally + * ignore resumption requests with flag + * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION (it's a new flag rather than + * a change to default behavior so that applications relying on this for + * security won't even compile against older library versions). + * + * 1.0.1 and later also have a function SSL_renegotiate_abbreviated() to + * request renegotiation but not a new session (s->new_session remains + * unset): for servers, this essentially just means that the + * SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION setting will be ignored. */ + if (s->new_session && + (s->options & SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)) { + if (!ssl_get_new_session(s, 1)) { + goto err; + } + } else { + i = ssl_get_prev_session(s, &early_ctx); + if (i == PENDING_SESSION) { + ret = PENDING_SESSION; + goto err; + } else if (i == -1) { + goto err; + } + + /* Only resume if the session's version matches the negotiated version: + * most clients do not accept a mismatch. */ + if (i == 1 && s->version == s->session->ssl_version) { + s->hit = 1; + } else { + /* No session was found or it was unacceptable. */ + if (!ssl_get_new_session(s, 1)) { + goto err; + } + } + } + + if (!CBS_get_u16_length_prefixed(&client_hello, &cipher_suites) || + !CBS_get_u8_length_prefixed(&client_hello, &compression_methods) || + CBS_len(&compression_methods) == 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_DECODE_ERROR); + goto f_err; + } + + /* TODO(davidben): Per spec, cipher_suites can never be empty (specified at + * the ClientHello structure level). This logic allows it to be empty if + * resuming a session. Can we always require non-empty? If a client sends + * empty cipher_suites because it's resuming a session, it could always fail + * to resume a session, so it's unlikely to actually work. */ + if (CBS_len(&cipher_suites) == 0 && CBS_len(&session_id) != 0) { + /* We need a cipher if we are not resuming a session. */ + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_NO_CIPHERS_SPECIFIED); + goto f_err; + } + + ciphers = ssl_bytes_to_cipher_list(s, &cipher_suites); + if (ciphers == NULL) { + goto err; + } + + /* If it is a hit, check that the cipher is in the list. */ + if (s->hit && CBS_len(&cipher_suites) > 0) { + size_t j; + int found_cipher = 0; + unsigned long id = s->session->cipher->id; + + for (j = 0; j < sk_SSL_CIPHER_num(ciphers); j++) { + c = sk_SSL_CIPHER_value(ciphers, j); + if (c->id == id) { + found_cipher = 1; + break; + } + } + + if (!found_cipher) { + /* we need to have the cipher in the cipher list if we are asked to reuse + * it */ + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, + SSL_R_REQUIRED_CIPHER_MISSING); + goto f_err; + } + } + + /* Only null compression is supported. */ + if (memchr(CBS_data(&compression_methods), 0, + CBS_len(&compression_methods)) == NULL) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, + SSL_R_NO_COMPRESSION_SPECIFIED); + goto f_err; + } + + /* TLS extensions. */ + if (s->version >= SSL3_VERSION && + !ssl_parse_clienthello_tlsext(s, &client_hello)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_PARSE_TLSEXT); + goto err; + } + + /* There should be nothing left over in the record. */ + if (CBS_len(&client_hello) != 0) { + /* wrong packet length */ + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_BAD_PACKET_LENGTH); + goto f_err; + } + + /* Given ciphers and SSL_get_ciphers, we must pick a cipher */ + if (!s->hit) { + if (ciphers == NULL) { + al = SSL_AD_ILLEGAL_PARAMETER; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_NO_CIPHERS_PASSED); + goto f_err; + } + + /* Let cert callback update server certificates if required */ + if (s->cert->cert_cb) { + int rv = s->cert->cert_cb(s, s->cert->cert_cb_arg); + if (rv == 0) { + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_CERT_CB_ERROR); + goto f_err; + } + if (rv < 0) { + s->rwstate = SSL_X509_LOOKUP; + goto err; + } + s->rwstate = SSL_NOTHING; + } + c = ssl3_choose_cipher(s, ciphers, ssl_get_cipher_preferences(s)); + + if (c == NULL) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_hello, SSL_R_NO_SHARED_CIPHER); + goto f_err; + } + s->s3->tmp.new_cipher = c; + } else { + /* Session-id reuse */ + s->s3->tmp.new_cipher = s->session->cipher; + } + + if ((!SSL_USE_SIGALGS(s) || !(s->verify_mode & SSL_VERIFY_PEER)) && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + goto f_err; + } + + /* we now have the following setup; + * client_random + * cipher_list - our prefered list of ciphers + * ciphers - the clients prefered list of ciphers + * compression - basically ignored right now + * ssl version is set - sslv3 + * s->session - The ssl session has been setup. + * s->hit - session reuse flag + * s->tmp.new_cipher - the new cipher to use. */ + + if (ret < 0) { + ret = -ret; + } + + if (0) { + f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + } + +err: + if (ciphers != NULL) { + sk_SSL_CIPHER_free(ciphers); + } + return ret; +} + +int ssl3_send_server_hello(SSL *s) { + uint8_t *buf; + uint8_t *p, *d; + int sl; + unsigned long l; + + if (s->state == SSL3_ST_SW_SRVR_HELLO_A) { + /* We only accept ChannelIDs on connections with ECDHE in order to avoid a + * known attack while we fix ChannelID itself. */ + if (s->s3->tlsext_channel_id_valid && + (s->s3->tmp.new_cipher->algorithm_mkey & SSL_kEECDH) == 0) { + s->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 (s->hit && s->s3->tlsext_channel_id_new && + s->session->original_handshake_hash_len == 0) { + s->s3->tlsext_channel_id_valid = 0; + } + + buf = (uint8_t *)s->init_buf->data; + /* Do the message type and length last */ + d = p = ssl_handshake_start(s); + + *(p++) = s->version >> 8; + *(p++) = s->version & 0xff; + + /* Random stuff */ + if (!ssl_fill_hello_random(s, 1, s->s3->server_random, SSL3_RANDOM_SIZE)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_hello, ERR_R_INTERNAL_ERROR); + return -1; + } + memcpy(p, s->s3->server_random, SSL3_RANDOM_SIZE); + p += SSL3_RANDOM_SIZE; + + /* There are several cases for the session ID to send + * back in the server hello: + * - For session reuse from the session cache, we send back the old session + * ID. + * - If stateless session reuse (using a session ticket) is successful, we + * send back the client's "session ID" (which doesn't actually identify + * the session). + * - If it is a new session, we send back the new session ID. + * - However, if we want the new session to be single-use, we send back a + * 0-length session ID. + * s->hit is non-zero in either case of session reuse, so the following + * won't overwrite an ID that we're supposed to send back. */ + if (!(s->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER) && !s->hit) { + s->session->session_id_length = 0; + } + + sl = s->session->session_id_length; + if (sl > (int)sizeof(s->session->session_id)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_hello, ERR_R_INTERNAL_ERROR); + return -1; + } + *(p++) = sl; + memcpy(p, s->session->session_id, sl); + p += sl; + + /* put the cipher */ + s2n(ssl3_get_cipher_value(s->s3->tmp.new_cipher), p); + + /* put the compression method */ + *(p++) = 0; + if (ssl_prepare_serverhello_tlsext(s) <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_hello, SSL_R_SERVERHELLO_TLSEXT); + return -1; + } + p = ssl_add_serverhello_tlsext(s, p, buf + SSL3_RT_MAX_PLAIN_LENGTH); + if (p == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_hello, ERR_R_INTERNAL_ERROR); + return -1; + } + + /* do the header */ + l = (p - d); + ssl_set_handshake_header(s, SSL3_MT_SERVER_HELLO, l); + s->state = SSL3_ST_SW_SRVR_HELLO_B; + } + + /* SSL3_ST_SW_SRVR_HELLO_B */ + return ssl_do_write(s); +} + +int ssl3_send_server_done(SSL *s) { + if (s->state == SSL3_ST_SW_SRVR_DONE_A) { + ssl_set_handshake_header(s, SSL3_MT_SERVER_DONE, 0); + s->state = SSL3_ST_SW_SRVR_DONE_B; + } + + /* SSL3_ST_SW_SRVR_DONE_B */ + return ssl_do_write(s); +} + +int ssl3_send_server_key_exchange(SSL *s) { + DH *dh = NULL, *dhp; + EC_KEY *ecdh = NULL, *ecdhp; + uint8_t *encodedPoint = NULL; + int encodedlen = 0; + uint16_t curve_id = 0; + BN_CTX *bn_ctx = NULL; + const char *psk_identity_hint = NULL; + size_t psk_identity_hint_len = 0; + EVP_PKEY *pkey; + uint8_t *p, *d; + int al, i; + unsigned long alg_k; + unsigned long alg_a; + int n; + CERT *cert; + BIGNUM *r[4]; + int nr[4], kn; + BUF_MEM *buf; + EVP_MD_CTX md_ctx; + + EVP_MD_CTX_init(&md_ctx); + if (s->state == SSL3_ST_SW_KEY_EXCH_A) { + alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + alg_a = s->s3->tmp.new_cipher->algorithm_auth; + cert = s->cert; + + buf = s->init_buf; + + r[0] = r[1] = r[2] = r[3] = NULL; + n = 0; + if (alg_a & SSL_aPSK) { + /* size for PSK identity hint */ + psk_identity_hint = s->psk_identity_hint; + if (psk_identity_hint) { + psk_identity_hint_len = strlen(psk_identity_hint); + } else { + psk_identity_hint_len = 0; + } + n += 2 + psk_identity_hint_len; + } + + if (alg_k & SSL_kEDH) { + dhp = cert->dh_tmp; + if (dhp == NULL && s->cert->dh_tmp_cb != NULL) { + dhp = s->cert->dh_tmp_cb(s, 0, 1024); + } + if (dhp == NULL) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, + SSL_R_MISSING_TMP_DH_KEY); + goto f_err; + } + + if (s->s3->tmp.dh != NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } + + dh = DHparams_dup(dhp); + if (dh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB); + goto err; + } + + s->s3->tmp.dh = dh; + if (dhp->pub_key == NULL || dhp->priv_key == NULL || + (s->options & SSL_OP_SINGLE_DH_USE)) { + if (!DH_generate_key(dh)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB); + goto err; + } + } else { + dh->pub_key = BN_dup(dhp->pub_key); + dh->priv_key = BN_dup(dhp->priv_key); + if (dh->pub_key == NULL || dh->priv_key == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_DH_LIB); + goto err; + } + } + + r[0] = dh->p; + r[1] = dh->g; + r[2] = dh->pub_key; + } else if (alg_k & SSL_kEECDH) { + const EC_GROUP *group; + + ecdhp = cert->ecdh_tmp; + if (s->cert->ecdh_tmp_auto) { + /* Get NID of appropriate shared curve */ + int nid = tls1_get_shared_curve(s); + if (nid != NID_undef) { + ecdhp = EC_KEY_new_by_curve_name(nid); + } + } else if (ecdhp == NULL && s->cert->ecdh_tmp_cb) { + ecdhp = s->cert->ecdh_tmp_cb(s, 0, 1024); + } + if (ecdhp == NULL) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, + SSL_R_MISSING_TMP_ECDH_KEY); + goto f_err; + } + + if (s->s3->tmp.ecdh != NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } + + /* Duplicate the ECDH structure. */ + if (ecdhp == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + + if (s->cert->ecdh_tmp_auto) { + ecdh = ecdhp; + } else { + ecdh = EC_KEY_dup(ecdhp); + if (ecdh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + } + + s->s3->tmp.ecdh = ecdh; + if (EC_KEY_get0_public_key(ecdh) == NULL || + EC_KEY_get0_private_key(ecdh) == NULL || + (s->options & SSL_OP_SINGLE_ECDH_USE)) { + if (!EC_KEY_generate_key(ecdh)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + } + + group = EC_KEY_get0_group(ecdh); + if (group == NULL || + EC_KEY_get0_public_key(ecdh) == NULL || + EC_KEY_get0_private_key(ecdh) == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + + /* We only support ephemeral ECDH keys over named (not generic) curves. */ + if (!tls1_ec_nid2curve_id(&curve_id, EC_GROUP_get_curve_name(group))) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, + SSL_R_UNSUPPORTED_ELLIPTIC_CURVE); + goto err; + } + + /* Encode the public key. First check the size of encoding and allocate + * memory accordingly. */ + encodedlen = + EC_POINT_point2oct(group, EC_KEY_get0_public_key(ecdh), + POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + + encodedPoint = (uint8_t *)OPENSSL_malloc(encodedlen * sizeof(uint8_t)); + bn_ctx = BN_CTX_new(); + if (encodedPoint == NULL || bn_ctx == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + encodedlen = EC_POINT_point2oct(group, EC_KEY_get0_public_key(ecdh), + POINT_CONVERSION_UNCOMPRESSED, + encodedPoint, encodedlen, bn_ctx); + + if (encodedlen == 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + + BN_CTX_free(bn_ctx); + bn_ctx = NULL; + + /* We only support named (not generic) curves in ECDH ephemeral key + * exchanges. In this situation, we need four additional bytes to encode + * the entire ServerECDHParams structure. */ + n += 4 + encodedlen; + + /* We'll generate the serverKeyExchange message explicitly so we can set + * these to NULLs */ + r[0] = NULL; + r[1] = NULL; + r[2] = NULL; + r[3] = NULL; + } else if (!(alg_k & SSL_kPSK)) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, + SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE); + goto f_err; + } + + for (i = 0; i < 4 && r[i] != NULL; i++) { + nr[i] = BN_num_bytes(r[i]); + n += 2 + nr[i]; + } + + if (ssl_cipher_has_server_public_key(s->s3->tmp.new_cipher)) { + pkey = ssl_get_sign_pkey(s, s->s3->tmp.new_cipher); + if (pkey == NULL) { + al = SSL_AD_DECODE_ERROR; + goto f_err; + } + kn = EVP_PKEY_size(pkey); + } else { + pkey = NULL; + kn = 0; + } + + if (!BUF_MEM_grow_clean(buf, n + SSL_HM_HEADER_LENGTH(s) + kn)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_LIB_BUF); + goto err; + } + d = p = ssl_handshake_start(s); + + for (i = 0; i < 4 && r[i] != NULL; i++) { + s2n(nr[i], p); + BN_bn2bin(r[i], p); + p += nr[i]; + } + + /* Note: ECDHE PSK ciphersuites use SSL_kEECDH and SSL_aPSK. When one of + * them is used, the server key exchange record needs to have both the + * psk_identity_hint and the ServerECDHParams. */ + if (alg_a & SSL_aPSK) { + /* copy PSK identity hint (if provided) */ + s2n(psk_identity_hint_len, p); + if (psk_identity_hint_len > 0) { + memcpy(p, psk_identity_hint, psk_identity_hint_len); + p += psk_identity_hint_len; + } + } + + if (alg_k & SSL_kEECDH) { + /* We only support named (not generic) curves. In this situation, the + * serverKeyExchange message has: + * [1 byte CurveType], [2 byte CurveName] + * [1 byte length of encoded point], followed by + * the actual encoded point itself. */ + *(p++) = NAMED_CURVE_TYPE; + *(p++) = (uint8_t)(curve_id >> 8); + *(p++) = (uint8_t)(curve_id & 0xff); + *(p++) = encodedlen; + memcpy(p, encodedPoint, encodedlen); + p += encodedlen; + OPENSSL_free(encodedPoint); + encodedPoint = NULL; + } + + /* not anonymous */ + if (pkey != NULL) { + /* n is the length of the params, they start at &(d[4]) and p points to + * the space at the end. */ + const EVP_MD *md; + size_t sig_len = EVP_PKEY_size(pkey); + + /* Determine signature algorithm. */ + if (SSL_USE_SIGALGS(s)) { + md = tls1_choose_signing_digest(s, pkey); + if (!tls12_get_sigandhash(p, pkey, md)) { + /* Should never happen */ + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, + ERR_R_INTERNAL_ERROR); + goto f_err; + } + p += 2; + } else if (pkey->type == EVP_PKEY_RSA) { + md = EVP_md5_sha1(); + } else { + md = EVP_sha1(); + } + + if (!EVP_DigestSignInit(&md_ctx, NULL, md, NULL, pkey) || + !EVP_DigestSignUpdate(&md_ctx, s->s3->client_random, + SSL3_RANDOM_SIZE) || + !EVP_DigestSignUpdate(&md_ctx, s->s3->server_random, + SSL3_RANDOM_SIZE) || + !EVP_DigestSignUpdate(&md_ctx, d, n) || + !EVP_DigestSignFinal(&md_ctx, &p[2], &sig_len)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_key_exchange, ERR_LIB_EVP); + goto err; + } + + s2n(sig_len, p); + n += sig_len + 2; + if (SSL_USE_SIGALGS(s)) { + n += 2; + } + } + + ssl_set_handshake_header(s, SSL3_MT_SERVER_KEY_EXCHANGE, n); + } + + s->state = SSL3_ST_SW_KEY_EXCH_B; + EVP_MD_CTX_cleanup(&md_ctx); + return ssl_do_write(s); + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); +err: + if (encodedPoint != NULL) { + OPENSSL_free(encodedPoint); + } + BN_CTX_free(bn_ctx); + EVP_MD_CTX_cleanup(&md_ctx); + return -1; +} + +int ssl3_send_certificate_request(SSL *s) { + uint8_t *p, *d; + size_t i; + int j, nl, off, n; + STACK_OF(X509_NAME) *sk = NULL; + X509_NAME *name; + BUF_MEM *buf; + + if (s->state == SSL3_ST_SW_CERT_REQ_A) { + buf = s->init_buf; + + d = p = ssl_handshake_start(s); + + /* get the list of acceptable cert types */ + p++; + n = ssl3_get_req_cert_type(s, p); + d[0] = n; + p += n; + n++; + + if (SSL_USE_SIGALGS(s)) { + const uint8_t *psigs; + nl = tls12_get_psigalgs(s, &psigs); + s2n(nl, p); + memcpy(p, psigs, nl); + p += nl; + n += nl + 2; + } + + off = n; + p += 2; + n += 2; + + sk = SSL_get_client_CA_list(s); + nl = 0; + if (sk != NULL) { + for (i = 0; i < sk_X509_NAME_num(sk); i++) { + name = sk_X509_NAME_value(sk, i); + j = i2d_X509_NAME(name, NULL); + if (!BUF_MEM_grow_clean(buf, SSL_HM_HEADER_LENGTH(s) + n + j + 2)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_certificate_request, ERR_R_BUF_LIB); + goto err; + } + p = ssl_handshake_start(s) + n; + s2n(j, p); + i2d_X509_NAME(name, &p); + n += 2 + j; + nl += 2 + j; + } + } + + /* else no CA names */ + p = ssl_handshake_start(s) + off; + s2n(nl, p); + + ssl_set_handshake_header(s, SSL3_MT_CERTIFICATE_REQUEST, n); + +#ifdef NETSCAPE_HANG_BUG + if (!SSL_IS_DTLS(s)) { + /* Prepare a ServerHelloDone in the same record. This is to workaround a + * hang in Netscape. */ + if (!BUF_MEM_grow_clean(buf, s->init_num + 4)) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_certificate_request, ERR_R_BUF_LIB); + goto err; + } + p = (uint8_t *)s->init_buf->data + s->init_num; + /* do the header */ + *(p++) = SSL3_MT_SERVER_DONE; + *(p++) = 0; + *(p++) = 0; + *(p++) = 0; + s->init_num += 4; + ssl3_finish_mac(s, p - 4, 4); + } +#endif + + s->state = SSL3_ST_SW_CERT_REQ_B; + } + + /* SSL3_ST_SW_CERT_REQ_B */ + return ssl_do_write(s); + +err: + return -1; +} + +int ssl3_get_client_key_exchange(SSL *s) { + int al, ok; + long n; + CBS client_key_exchange; + unsigned long alg_k; + unsigned long alg_a; + uint8_t *premaster_secret = NULL; + size_t premaster_secret_len = 0; + RSA *rsa = NULL; + uint8_t *decrypt_buf = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *pub = NULL; + DH *dh_srvr; + + EC_KEY *srvr_ecdh = NULL; + EVP_PKEY *clnt_pub_pkey = NULL; + EC_POINT *clnt_ecpoint = NULL; + BN_CTX *bn_ctx = NULL; + unsigned int psk_len = 0; + uint8_t psk[PSK_MAX_PSK_LEN]; + + n = s->method->ssl_get_message(s, SSL3_ST_SR_KEY_EXCH_A, + SSL3_ST_SR_KEY_EXCH_B, + SSL3_MT_CLIENT_KEY_EXCHANGE, 2048, /* ??? */ + SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + CBS_init(&client_key_exchange, s->init_msg, n); + + alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + alg_a = s->s3->tmp.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, ssl3_get_client_key_exchange, SSL_R_DECODE_ERROR); + al = SSL_AD_DECODE_ERROR; + goto f_err; + } + + if (s->psk_server_callback == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + 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, ssl3_get_client_key_exchange, + SSL_R_DATA_LENGTH_TOO_LONG); + al = SSL_AD_ILLEGAL_PARAMETER; + goto f_err; + } + + if (!CBS_strdup(&psk_identity, &s->session->psk_identity)) { + al = SSL_AD_INTERNAL_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto f_err; + } + + /* Look up the key for the identity. */ + psk_len = + s->psk_server_callback(s, s->session->psk_identity, psk, sizeof(psk)); + if (psk_len > PSK_MAX_PSK_LEN) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + 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, ssl3_get_client_key_exchange, + 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) { + CBS encrypted_premaster_secret; + uint8_t rand_premaster_secret[SSL_MAX_MASTER_KEY_LENGTH]; + uint8_t good; + size_t rsa_size, decrypt_len, premaster_index, j; + + pkey = s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey; + if (pkey == NULL || pkey->type != EVP_PKEY_RSA || pkey->pkey.rsa == NULL) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + SSL_R_MISSING_RSA_CERTIFICATE); + goto f_err; + } + rsa = pkey->pkey.rsa; + + /* TLS and [incidentally] DTLS{0xFEFF} */ + if (s->version > SSL3_VERSION) { + CBS copy = client_key_exchange; + if (!CBS_get_u16_length_prefixed(&client_key_exchange, + &encrypted_premaster_secret) || + CBS_len(&client_key_exchange) != 0) { + if (!(s->options & SSL_OP_TLS_D5_BUG)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG); + goto f_err; + } else { + encrypted_premaster_secret = copy; + } + } + } else { + encrypted_premaster_secret = client_key_exchange; + } + + /* Reject overly short RSA keys because we want to be sure that the buffer + * size makes it safe to iterate over the entire size of a premaster secret + * (SSL_MAX_MASTER_KEY_LENGTH). The actual expected size is larger due to + * RSA padding, but the bound is sufficient to be safe. */ + rsa_size = RSA_size(rsa); + if (rsa_size < SSL_MAX_MASTER_KEY_LENGTH) { + al = SSL_AD_DECRYPT_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + SSL_R_DECRYPTION_FAILED); + goto f_err; + } + + /* We must not leak whether a decryption failure occurs because of + * Bleichenbacher's attack on PKCS #1 v1.5 RSA padding (see RFC 2246, + * section 7.4.7.1). The code follows that advice of the TLS RFC and + * generates a random premaster secret for the case that the decrypt fails. + * See https://tools.ietf.org/html/rfc5246#section-7.4.7.1 */ + if (!RAND_bytes(rand_premaster_secret, sizeof(rand_premaster_secret))) { + goto err; + } + + /* Allocate a buffer large enough for an RSA decryption. */ + decrypt_buf = OPENSSL_malloc(rsa_size); + if (decrypt_buf == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + /* Decrypt with no padding. PKCS#1 padding will be removed as part of the + * timing-sensitive code below. */ + if (!RSA_decrypt(rsa, &decrypt_len, decrypt_buf, rsa_size, + CBS_data(&encrypted_premaster_secret), + CBS_len(&encrypted_premaster_secret), RSA_NO_PADDING)) { + goto err; + } + if (decrypt_len != rsa_size) { + /* This should never happen, but do a check so we do not read + * uninitialized memory. */ + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_INTERNAL_ERROR); + goto err; + } + + /* Remove the PKCS#1 padding and adjust |decrypt_len| as appropriate. + * |good| will be 0xff if the premaster is acceptable and zero otherwise. + * */ + good = + constant_time_eq_int_8(RSA_message_index_PKCS1_type_2( + decrypt_buf, decrypt_len, &premaster_index), + 1); + decrypt_len = decrypt_len - premaster_index; + + /* decrypt_len should be SSL_MAX_MASTER_KEY_LENGTH. */ + good &= constant_time_eq_8(decrypt_len, SSL_MAX_MASTER_KEY_LENGTH); + + /* Copy over the unpadded premaster. Whatever the value of + * |decrypt_good_mask|, copy as if the premaster were the right length. It + * is important the memory access pattern be constant. */ + premaster_secret = + BUF_memdup(decrypt_buf + (rsa_size - SSL_MAX_MASTER_KEY_LENGTH), + SSL_MAX_MASTER_KEY_LENGTH); + if (premaster_secret == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + OPENSSL_free(decrypt_buf); + decrypt_buf = NULL; + + /* If the version in the decrypted pre-master secret is correct then + * version_good will be 0xff, otherwise it'll be zero. The + * Klima-Pokorny-Rosa extension of Bleichenbacher's attack + * (http://eprint.iacr.org/2003/052/) exploits the version number check as + * a "bad version oracle". Thus version checks are done in constant time + * and are treated like any other decryption error. */ + good &= constant_time_eq_8(premaster_secret[0], + (unsigned)(s->client_version >> 8)); + good &= constant_time_eq_8(premaster_secret[1], + (unsigned)(s->client_version & 0xff)); + + /* Now copy rand_premaster_secret over premaster_secret using + * decrypt_good_mask. */ + for (j = 0; j < sizeof(rand_premaster_secret); j++) { + premaster_secret[j] = constant_time_select_8(good, premaster_secret[j], + rand_premaster_secret[j]); + } + + premaster_secret_len = sizeof(rand_premaster_secret); + } else if (alg_k & SSL_kEDH) { + CBS dh_Yc; + int dh_len; + + if (!CBS_get_u16_length_prefixed(&client_key_exchange, &dh_Yc) || + CBS_len(&dh_Yc) == 0 || CBS_len(&client_key_exchange) != 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG); + al = SSL_R_DECODE_ERROR; + goto f_err; + } + + if (s->s3->tmp.dh == NULL) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + SSL_R_MISSING_TMP_DH_KEY); + goto f_err; + } + dh_srvr = s->s3->tmp.dh; + + pub = BN_bin2bn(CBS_data(&dh_Yc), CBS_len(&dh_Yc), NULL); + if (pub == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_BN_LIB); + goto err; + } + + /* Allocate a buffer for the premaster secret. */ + premaster_secret = OPENSSL_malloc(DH_size(dh_srvr)); + if (premaster_secret == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + dh_len = DH_compute_key(premaster_secret, pub, dh_srvr); + if (dh_len <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_DH_LIB); + BN_clear_free(pub); + goto err; + } + + DH_free(s->s3->tmp.dh); + s->s3->tmp.dh = NULL; + BN_clear_free(pub); + pub = NULL; + + premaster_secret_len = dh_len; + } else if (alg_k & SSL_kEECDH) { + int field_size = 0, ecdh_len; + const EC_KEY *tkey; + const EC_GROUP *group; + const BIGNUM *priv_key; + CBS ecdh_Yc; + + /* initialize structures for server's ECDH key pair */ + srvr_ecdh = EC_KEY_new(); + if (srvr_ecdh == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + /* Use the ephermeral values we saved when generating the ServerKeyExchange + * msg. */ + tkey = s->s3->tmp.ecdh; + + group = EC_KEY_get0_group(tkey); + priv_key = EC_KEY_get0_private_key(tkey); + + if (!EC_KEY_set_group(srvr_ecdh, group) || + !EC_KEY_set_private_key(srvr_ecdh, priv_key)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_EC_LIB); + goto err; + } + + /* Let's get client's public key */ + clnt_ecpoint = EC_POINT_new(group); + if (clnt_ecpoint == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + /* Get client's public key from encoded point in the ClientKeyExchange + * message. */ + if (!CBS_get_u8_length_prefixed(&client_key_exchange, &ecdh_Yc) || + CBS_len(&client_key_exchange) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, SSL_R_DECODE_ERROR); + goto f_err; + } + + bn_ctx = BN_CTX_new(); + if (bn_ctx == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!EC_POINT_oct2point(group, clnt_ecpoint, CBS_data(&ecdh_Yc), + CBS_len(&ecdh_Yc), bn_ctx)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_EC_LIB); + goto err; + } + + /* Allocate a buffer for both the secret and the PSK. */ + field_size = EC_GROUP_get_degree(group); + if (field_size <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + + ecdh_len = (field_size + 7) / 8; + premaster_secret = OPENSSL_malloc(ecdh_len); + if (premaster_secret == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + + /* Compute the shared pre-master secret */ + ecdh_len = ECDH_compute_key(premaster_secret, ecdh_len, clnt_ecpoint, + srvr_ecdh, NULL); + if (ecdh_len <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, ERR_R_ECDH_LIB); + goto err; + } + + EVP_PKEY_free(clnt_pub_pkey); + clnt_pub_pkey = NULL; + EC_POINT_free(clnt_ecpoint); + clnt_ecpoint = NULL; + EC_KEY_free(srvr_ecdh); + srvr_ecdh = NULL; + BN_CTX_free(bn_ctx); + bn_ctx = NULL; + EC_KEY_free(s->s3->tmp.ecdh); + s->s3->tmp.ecdh = NULL; + + premaster_secret_len = ecdh_len; + } 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, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + memset(premaster_secret, 0, premaster_secret_len); + } else { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + 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; + + if (!CBB_init(&new_premaster, 2 + psk_len + 2 + premaster_secret_len)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_key_exchange, + ERR_R_MALLOC_FAILURE); + goto err; + } + if (!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, ssl3_get_client_key_exchange, + ERR_R_INTERNAL_ERROR); + 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 */ + s->session->master_key_length = s->enc_method->generate_master_secret( + s, s->session->master_key, premaster_secret, premaster_secret_len); + if (s->session->master_key_length == 0) { + goto err; + } + s->session->extended_master_secret = s->s3->tmp.extended_master_secret; + + OPENSSL_cleanse(premaster_secret, premaster_secret_len); + OPENSSL_free(premaster_secret); + return 1; + +f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); +err: + if (premaster_secret) { + if (premaster_secret_len) { + OPENSSL_cleanse(premaster_secret, premaster_secret_len); + } + OPENSSL_free(premaster_secret); + } + if (decrypt_buf) { + OPENSSL_free(decrypt_buf); + } + EVP_PKEY_free(clnt_pub_pkey); + EC_POINT_free(clnt_ecpoint); + if (srvr_ecdh != NULL) { + EC_KEY_free(srvr_ecdh); + } + BN_CTX_free(bn_ctx); + + return -1; +} + +int ssl3_get_cert_verify(SSL *s) { + int al, ok, ret = 0; + long n; + CBS certificate_verify, signature; + X509 *peer = s->session->peer; + EVP_PKEY *pkey = NULL; + const EVP_MD *md = NULL; + uint8_t digest[EVP_MAX_MD_SIZE]; + size_t digest_length; + EVP_PKEY_CTX *pctx = NULL; + + /* Only RSA and ECDSA client certificates are supported, so a + * CertificateVerify is required if and only if there's a client certificate. + * */ + if (peer == NULL) { + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + return -1; + } + return 1; + } + + n = s->method->ssl_get_message( + s, SSL3_ST_SR_CERT_VRFY_A, SSL3_ST_SR_CERT_VRFY_B, + SSL3_MT_CERTIFICATE_VERIFY, SSL3_RT_MAX_PLAIN_LENGTH, + SSL_GET_MESSAGE_DONT_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + /* Filter out unsupported certificate types. */ + pkey = X509_get_pubkey(peer); + if (!(X509_certificate_type(peer, pkey) & EVP_PKT_SIGN) || + (pkey->type != EVP_PKEY_RSA && pkey->type != EVP_PKEY_EC)) { + al = SSL_AD_UNSUPPORTED_CERTIFICATE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_verify, + SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE); + goto f_err; + } + + CBS_init(&certificate_verify, s->init_msg, n); + + /* Determine the digest type if needbe. */ + if (SSL_USE_SIGALGS(s) && + !tls12_check_peer_sigalg(&md, &al, s, &certificate_verify, pkey)) { + goto f_err; + } + + /* Compute the digest. */ + if (!ssl3_cert_verify_hash(s, digest, &digest_length, &md, pkey)) { + goto err; + } + + /* The handshake buffer is no longer necessary, and we may hash the current + * message.*/ + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + goto err; + } + ssl3_hash_current_message(s); + + /* 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, ssl3_get_cert_verify, SSL_R_DECODE_ERROR); + goto f_err; + } + + pctx = EVP_PKEY_CTX_new(pkey, NULL); + if (pctx == NULL) { + goto err; + } + if (!EVP_PKEY_verify_init(pctx) || + !EVP_PKEY_CTX_set_signature_md(pctx, md) || + !EVP_PKEY_verify(pctx, CBS_data(&signature), CBS_len(&signature), digest, + digest_length)) { + al = SSL_AD_DECRYPT_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_cert_verify, SSL_R_BAD_SIGNATURE); + goto f_err; + } + + ret = 1; + + if (0) { + f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + } + +err: + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); + + return ret; +} + +int ssl3_get_client_certificate(SSL *s) { + int i, ok, al, ret = -1; + X509 *x = NULL; + unsigned long n; + STACK_OF(X509) *sk = NULL; + SHA256_CTX sha256; + CBS certificate_msg, certificate_list; + int is_first_certificate = 1; + + n = s->method->ssl_get_message(s, SSL3_ST_SR_CERT_A, SSL3_ST_SR_CERT_B, -1, + s->max_cert_list, SSL_GET_MESSAGE_HASH_MESSAGE, + &ok); + + if (!ok) { + return n; + } + + if (s->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) { + if ((s->verify_mode & SSL_VERIFY_PEER) && + (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, + SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); + al = SSL_AD_HANDSHAKE_FAILURE; + goto f_err; + } + + /* If tls asked for a client cert, the client must return a 0 list */ + if (s->version > SSL3_VERSION && s->s3->tmp.cert_request) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, + SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST); + al = SSL_AD_UNEXPECTED_MESSAGE; + goto f_err; + } + s->s3->tmp.reuse_message = 1; + + return 1; + } + + if (s->s3->tmp.message_type != SSL3_MT_CERTIFICATE) { + al = SSL_AD_UNEXPECTED_MESSAGE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, + SSL_R_WRONG_MESSAGE_TYPE); + goto f_err; + } + + CBS_init(&certificate_msg, s->init_msg, n); + + sk = sk_X509_new_null(); + if (sk == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CBS_get_u24_length_prefixed(&certificate_msg, &certificate_list) || + CBS_len(&certificate_msg) != 0) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, SSL_R_DECODE_ERROR); + goto f_err; + } + + while (CBS_len(&certificate_list) > 0) { + CBS certificate; + const uint8_t *data; + + if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, SSL_R_DECODE_ERROR); + goto f_err; + } + + if (is_first_certificate && s->ctx->retain_only_sha256_of_client_certs) { + /* If this is the first certificate, and we don't want to keep peer + * certificates in memory, then we hash it right away. */ + SHA256_Init(&sha256); + SHA256_Update(&sha256, CBS_data(&certificate), CBS_len(&certificate)); + SHA256_Final(s->session->peer_sha256, &sha256); + s->session->peer_sha256_valid = 1; + } + is_first_certificate = 0; + + data = CBS_data(&certificate); + x = d2i_X509(NULL, &data, CBS_len(&certificate)); + if (x == NULL) { + al = SSL_AD_BAD_CERTIFICATE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, ERR_R_ASN1_LIB); + goto f_err; + } + if (data != CBS_data(&certificate) + CBS_len(&certificate)) { + al = SSL_AD_DECODE_ERROR; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, + SSL_R_CERT_LENGTH_MISMATCH); + goto f_err; + } + if (!sk_X509_push(sk, x)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, ERR_R_MALLOC_FAILURE); + goto err; + } + x = NULL; + } + + if (sk_X509_num(sk) <= 0) { + /* TLS does not mind 0 certs returned */ + if (s->version == SSL3_VERSION) { + al = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, + SSL_R_NO_CERTIFICATES_RETURNED); + goto f_err; + } + /* Fail for TLS only if we required a certificate */ + else if ((s->verify_mode & SSL_VERIFY_PEER) && + (s->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, + SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); + al = SSL_AD_HANDSHAKE_FAILURE; + goto f_err; + } + /* No client certificate so digest cached records */ + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + al = SSL_AD_INTERNAL_ERROR; + goto f_err; + } + } else { + i = ssl_verify_cert_chain(s, sk); + if (i <= 0) { + al = ssl_verify_alarm_type(s->verify_result); + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, + SSL_R_CERTIFICATE_VERIFY_FAILED); + goto f_err; + } + } + + if (s->session->peer != NULL) { + /* This should not be needed */ + X509_free(s->session->peer); + } + + s->session->peer = sk_X509_shift(sk); + s->session->verify_result = s->verify_result; + + /* With the current implementation, sess_cert will always be NULL when we + * arrive here. */ + if (s->session->sess_cert == NULL) { + s->session->sess_cert = ssl_sess_cert_new(); + if (s->session->sess_cert == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_client_certificate, ERR_R_MALLOC_FAILURE); + goto err; + } + } + if (s->session->sess_cert->cert_chain != NULL) { + sk_X509_pop_free(s->session->sess_cert->cert_chain, X509_free); + } + s->session->sess_cert->cert_chain = sk; + /* Inconsistency alert: cert_chain does *not* include the peer's own + * certificate, while we do include it in s3_clnt.c */ + + sk = NULL; + + ret = 1; + + if (0) { + f_err: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + } + +err: + if (x != NULL) { + X509_free(x); + } + if (sk != NULL) { + sk_X509_pop_free(sk, X509_free); + } + return ret; +} + +int ssl3_send_server_certificate(SSL *s) { + CERT_PKEY *cpk; + + if (s->state == SSL3_ST_SW_CERT_A) { + cpk = ssl_get_server_send_pkey(s); + if (cpk == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl3_send_server_certificate, + ERR_R_INTERNAL_ERROR); + return 0; + } + + ssl3_output_cert_chain(s, cpk); + s->state = SSL3_ST_SW_CERT_B; + } + + /* SSL3_ST_SW_CERT_B */ + return ssl_do_write(s); +} + +/* send a new session ticket (not necessarily for a new session) */ +int ssl3_send_new_session_ticket(SSL *s) { + if (s->state == SSL3_ST_SW_SESSION_TICKET_A) { + uint8_t *session; + size_t session_len; + uint8_t *p, *macstart; + int len; + unsigned int hlen; + EVP_CIPHER_CTX ctx; + HMAC_CTX hctx; + SSL_CTX *tctx = s->initial_ctx; + uint8_t iv[EVP_MAX_IV_LENGTH]; + uint8_t key_name[16]; + /* The maximum overhead of encrypting the session is 16 (key name) + IV + + * one block of encryption overhead + HMAC. */ + const size_t max_ticket_overhead = + 16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE; + + /* Serialize the SSL_SESSION to be encoded into the ticket. */ + if (!SSL_SESSION_to_bytes_for_ticket(s->session, &session, &session_len)) { + return -1; + } + + /* If the session is too long, emit a dummy value rather than abort the + * connection. */ + if (session_len > 0xFFFF - max_ticket_overhead) { + static const char kTicketPlaceholder[] = "TICKET TOO LARGE"; + const size_t placeholder_len = strlen(kTicketPlaceholder); + + OPENSSL_free(session); + + p = ssl_handshake_start(s); + /* Emit ticket_lifetime_hint. */ + l2n(0, p); + /* Emit ticket. */ + s2n(placeholder_len, p); + memcpy(p, kTicketPlaceholder, placeholder_len); + p += placeholder_len; + + len = p - ssl_handshake_start(s); + ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len); + s->state = SSL3_ST_SW_SESSION_TICKET_B; + return ssl_do_write(s); + } + + /* Grow buffer if need be: the length calculation is as follows: + * handshake_header_length + 4 (ticket lifetime hint) + 2 (ticket length) + + * max_ticket_overhead + * session_length */ + if (!BUF_MEM_grow(s->init_buf, SSL_HM_HEADER_LENGTH(s) + 6 + + max_ticket_overhead + session_len)) { + OPENSSL_free(session); + return -1; + } + p = ssl_handshake_start(s); + EVP_CIPHER_CTX_init(&ctx); + HMAC_CTX_init(&hctx); + /* Initialize HMAC and cipher contexts. If callback present it does all the + * work otherwise use generated values from parent ctx. */ + if (tctx->tlsext_ticket_key_cb) { + if (tctx->tlsext_ticket_key_cb(s, key_name, iv, &ctx, &hctx, 1) < 0) { + OPENSSL_free(session); + EVP_CIPHER_CTX_cleanup(&ctx); + HMAC_CTX_cleanup(&hctx); + return -1; + } + } 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)) { + OPENSSL_free(session); + EVP_CIPHER_CTX_cleanup(&ctx); + HMAC_CTX_cleanup(&hctx); + return -1; + } + memcpy(key_name, tctx->tlsext_tick_key_name, 16); + } + + /* Ticket lifetime hint (advisory only): We leave this unspecified for + * resumed session (for simplicity), and guess that tickets for new + * sessions will live as long as their sessions. */ + l2n(s->hit ? 0 : s->session->timeout, p); + + /* Skip ticket length for now */ + p += 2; + /* Output key name */ + macstart = p; + memcpy(p, key_name, 16); + p += 16; + /* output IV */ + memcpy(p, iv, EVP_CIPHER_CTX_iv_length(&ctx)); + p += EVP_CIPHER_CTX_iv_length(&ctx); + /* Encrypt session data */ + EVP_EncryptUpdate(&ctx, p, &len, session, session_len); + p += len; + EVP_EncryptFinal_ex(&ctx, p, &len); + p += len; + EVP_CIPHER_CTX_cleanup(&ctx); + + HMAC_Update(&hctx, macstart, p - macstart); + HMAC_Final(&hctx, p, &hlen); + HMAC_CTX_cleanup(&hctx); + + p += hlen; + /* Now write out lengths: p points to end of data written */ + /* Total length */ + len = p - ssl_handshake_start(s); + /* Skip ticket lifetime hint */ + p = ssl_handshake_start(s) + 4; + s2n(len - 6, p); + ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len); + s->state = SSL3_ST_SW_SESSION_TICKET_B; + OPENSSL_free(session); + } + + /* SSL3_ST_SW_SESSION_TICKET_B */ + return ssl_do_write(s); +} + +/* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It + * sets the next_proto member in s if found */ +int ssl3_get_next_proto(SSL *s) { + int ok; + long n; + CBS next_protocol, selected_protocol, padding; + + /* Clients cannot send a NextProtocol message if we didn't see the extension + * in their ClientHello */ + if (!s->s3->next_proto_neg_seen) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_next_proto, + SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION); + return -1; + } + + n = s->method->ssl_get_message(s, SSL3_ST_SR_NEXT_PROTO_A, + SSL3_ST_SR_NEXT_PROTO_B, SSL3_MT_NEXT_PROTO, + 514, /* See the payload format below */ + SSL_GET_MESSAGE_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + /* s->state doesn't reflect whether ChangeCipherSpec has been received in + * this handshake, but s->s3->change_cipher_spec does (will be reset by + * ssl3_get_finished). + * + * TODO(davidben): Is this check now redundant with + * SSL3_FLAGS_EXPECT_CCS? */ + if (!s->s3->change_cipher_spec) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_next_proto, + SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS); + return -1; + } + + CBS_init(&next_protocol, s->init_msg, n); + + /* The payload looks like: + * uint8 proto_len; + * uint8 proto[proto_len]; + * uint8 padding_len; + * uint8 padding[padding_len]; */ + if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) || + !CBS_get_u8_length_prefixed(&next_protocol, &padding) || + CBS_len(&next_protocol) != 0 || + !CBS_stow(&selected_protocol, &s->next_proto_negotiated, + &s->next_proto_negotiated_len)) { + return 0; + } + + return 1; +} + +/* ssl3_get_channel_id reads and verifies a ClientID handshake message. */ +int ssl3_get_channel_id(SSL *s) { + int ret = -1, ok; + long n; + EVP_MD_CTX md_ctx; + uint8_t channel_id_hash[SHA256_DIGEST_LENGTH]; + unsigned int channel_id_hash_len; + const uint8_t *p; + uint16_t extension_type, expected_extension_type; + EC_GROUP *p256 = NULL; + EC_KEY *key = NULL; + EC_POINT *point = NULL; + ECDSA_SIG sig; + BIGNUM x, y; + CBS encrypted_extensions, extension; + + n = s->method->ssl_get_message( + s, SSL3_ST_SR_CHANNEL_ID_A, SSL3_ST_SR_CHANNEL_ID_B, + SSL3_MT_ENCRYPTED_EXTENSIONS, 2 + 2 + TLSEXT_CHANNEL_ID_SIZE, + SSL_GET_MESSAGE_DONT_HASH_MESSAGE, &ok); + + if (!ok) { + return n; + } + + /* Before incorporating the EncryptedExtensions message to the handshake + * hash, compute the hash that should have been signed. */ + channel_id_hash_len = sizeof(channel_id_hash); + EVP_MD_CTX_init(&md_ctx); + if (!EVP_DigestInit_ex(&md_ctx, EVP_sha256(), NULL) || + !tls1_channel_id_hash(&md_ctx, s) || + !EVP_DigestFinal(&md_ctx, channel_id_hash, &channel_id_hash_len)) { + EVP_MD_CTX_cleanup(&md_ctx); + return -1; + } + EVP_MD_CTX_cleanup(&md_ctx); + assert(channel_id_hash_len == SHA256_DIGEST_LENGTH); + + ssl3_hash_current_message(s); + + /* s->state doesn't reflect whether ChangeCipherSpec has been received in + * this handshake, but s->s3->change_cipher_spec does (will be reset by + * ssl3_get_finished). + * + * TODO(davidben): Is this check now redundant with SSL3_FLAGS_EXPECT_CCS? */ + if (!s->s3->change_cipher_spec) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_channel_id, + SSL_R_GOT_CHANNEL_ID_BEFORE_A_CCS); + return -1; + } + + CBS_init(&encrypted_extensions, s->init_msg, n); + + /* EncryptedExtensions could include multiple extensions, but the only + * extension that could be negotiated is ChannelID, so there can only be one + * entry. + * + * The payload looks like: + * uint16 extension_type + * uint16 extension_len; + * uint8 x[32]; + * uint8 y[32]; + * uint8 r[32]; + * uint8 s[32]; */ + expected_extension_type = TLSEXT_TYPE_channel_id; + if (s->s3->tlsext_channel_id_new) { + expected_extension_type = TLSEXT_TYPE_channel_id_new; + } + + if (!CBS_get_u16(&encrypted_extensions, &extension_type) || + !CBS_get_u16_length_prefixed(&encrypted_extensions, &extension) || + CBS_len(&encrypted_extensions) != 0 || + extension_type != expected_extension_type || + CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_channel_id, SSL_R_INVALID_MESSAGE); + return -1; + } + + p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + if (!p256) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_channel_id, SSL_R_NO_P256_SUPPORT); + return -1; + } + + BN_init(&x); + BN_init(&y); + sig.r = BN_new(); + sig.s = BN_new(); + + 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 || !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) { + goto err; + } + + key = EC_KEY_new(); + if (!key || !EC_KEY_set_group(key, p256) || + !EC_KEY_set_public_key(key, point)) { + goto err; + } + + /* We stored the handshake hash in |tlsext_channel_id| the first time that we + * were called. */ + if (!ECDSA_do_verify(channel_id_hash, channel_id_hash_len, &sig, key)) { + OPENSSL_PUT_ERROR(SSL, ssl3_get_channel_id, + SSL_R_CHANNEL_ID_SIGNATURE_INVALID); + s->s3->tlsext_channel_id_valid = 0; + goto err; + } + + memcpy(s->s3->tlsext_channel_id, p, 64); + ret = 1; + +err: + BN_free(&x); + BN_free(&y); + BN_free(sig.r); + BN_free(sig.s); + if (key) { + EC_KEY_free(key); + } + if (point) { + EC_POINT_free(point); + } + if (p256) { + EC_GROUP_free(p256); + } + return ret; +} diff --git a/src/ssl/ssl_algs.c b/src/ssl/ssl_algs.c new file mode 100644 index 0000000..6ec88bf --- /dev/null +++ b/src/ssl/ssl_algs.c @@ -0,0 +1,71 @@ +/* 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 "ssl_locl.h" + +#include <openssl/crypto.h> + +extern const ERR_STRING_DATA SSL_error_string_data[]; + +int SSL_library_init(void) { + CRYPTO_library_init(); + ERR_load_crypto_strings(); + ERR_load_strings(SSL_error_string_data); + return 1; +} + +void SSL_load_error_strings(void) { +} diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c new file mode 100644 index 0000000..d39da87 --- /dev/null +++ b/src/ssl/ssl_asn1.c @@ -0,0 +1,591 @@ +/* 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 <limits.h> +#include <string.h> + +#include <openssl/bytestring.h> +#include <openssl/err.h> +#include <openssl/x509.h> + +#include "ssl_locl.h" + + +/* An SSL_SESSION is serialized as the following ASN.1 structure: + * + * SSLSession ::= SEQUENCE { + * version INTEGER (1), -- ignored + * sslVersion INTEGER, -- protocol version number + * cipher OCTET STRING, -- two bytes long + * sessionID OCTET STRING, + * masterKey OCTET STRING, + * time [1] INTEGER OPTIONAL, -- seconds since UNIX epoch + * timeout [2] INTEGER OPTIONAL, -- 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, + * } + * + * Note: historically this serialization has included other optional + * fields. Their presense 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, */ + +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 int SSL_SESSION_to_bytes_full(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; + } + + if (!CBB_init(&cbb, 0)) { + return 0; + } + + if (!CBB_add_asn1(&cbb, &session, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1_uint64(&session, SSL_SESSION_ASN1_VERSION) || + !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)) { + OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (in->time != 0) { + if (!CBB_add_asn1(&session, &child, kTimeTag) || + !CBB_add_asn1_uint64(&child, in->time)) { + OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->timeout != 0) { + if (!CBB_add_asn1(&session, &child, kTimeoutTag) || + !CBB_add_asn1_uint64(&child, in->timeout)) { + OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + /* The peer certificate is only serialized if the SHA-256 isn't + * serialized instead. */ + if (in->peer && !in->peer_sha256_valid) { + uint8_t *buf; + int len = i2d_X509(in->peer, NULL); + if (len < 0) { + goto err; + } + if (!CBB_add_asn1(&session, &child, kPeerTag) || + !CBB_add_space(&child, &buf, len)) { + OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); + goto err; + } + if (buf != NULL && i2d_X509(in->peer, &buf) < 0) { + 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (in->tlsext_tick) { + 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, 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, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (!CBB_finish(&cbb, out_data, out_len)) { + OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE); + goto err; + } + return 1; + + err: + CBB_cleanup(&cbb); + return 0; +} + +int SSL_SESSION_to_bytes(SSL_SESSION *in, uint8_t **out_data, size_t *out_len) { + return SSL_SESSION_to_bytes_full(in, out_data, out_len, 0); +} + +int SSL_SESSION_to_bytes_for_ticket(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, i2d_SSL_SESSION, ERR_R_OVERFLOW); + return -1; + } + + if (pp) { + memcpy(*pp, out, len); + *pp += len; + } + OPENSSL_free(out); + + return len; +} + +/* d2i_SSL_SESSION_get_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 d2i_SSL_SESSION_get_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, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + return 0; + } + if (present) { + if (CBS_contains_zero_byte(&value)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + return 0; + } + if (!CBS_strdup(&value, out)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_MALLOC_FAILURE); + return 0; + } + } else if (*out) { + OPENSSL_free(*out); + *out = NULL; + } + return 1; +} + +/* d2i_SSL_SESSION_get_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 d2i_SSL_SESSION_get_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, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + return 0; + } + if (!CBS_stow(&value, out_ptr, out_len)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_MALLOC_FAILURE); + return 0; + } + return 1; +} + +SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) { + SSL_SESSION *ret = NULL; + CBS cbs, session, cipher, session_id, master_key; + CBS peer, sid_ctx, peer_sha256, original_handshake_hash; + int has_peer, has_peer_sha256, extended_master_secret; + uint64_t version, ssl_version; + uint64_t session_time, timeout, verify_result, ticket_lifetime_hint; + + if (a && *a) { + ret = *a; + } else { + ret = SSL_SESSION_new(); + if (ret == NULL) { + goto err; + } + } + + CBS_init(&cbs, *pp, length); + if (!CBS_get_asn1(&cbs, &session, CBS_ASN1_SEQUENCE) || + !CBS_get_asn1_uint64(&session, &version) || + !CBS_get_asn1_uint64(&session, &ssl_version) || + !CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) || + !CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING) || + !CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING) || + !CBS_get_optional_asn1_uint64(&session, &session_time, kTimeTag, + time(NULL)) || + !CBS_get_optional_asn1_uint64(&session, &timeout, kTimeoutTag, 3) || + !CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag) || + !CBS_get_optional_asn1_octet_string(&session, &sid_ctx, NULL, + kSessionIDContextTag) || + !CBS_get_optional_asn1_uint64(&session, &verify_result, kVerifyResultTag, + X509_V_OK)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + if (!d2i_SSL_SESSION_get_string(&session, &ret->tlsext_hostname, + kHostNameTag) || + !d2i_SSL_SESSION_get_string(&session, &ret->psk_identity, + kPSKIdentityTag)) { + goto err; + } + if (!CBS_get_optional_asn1_uint64(&session, &ticket_lifetime_hint, + kTicketLifetimeHintTag, 0)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + if (!d2i_SSL_SESSION_get_octet_string(&session, &ret->tlsext_tick, + &ret->tlsext_ticklen, kTicketTag)) { + goto err; + } + if (!CBS_get_optional_asn1_octet_string(&session, &peer_sha256, + &has_peer_sha256, kPeerSHA256Tag) || + !CBS_get_optional_asn1_octet_string(&session, &original_handshake_hash, + NULL, kOriginalHandshakeHashTag)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + if (!d2i_SSL_SESSION_get_octet_string( + &session, &ret->tlsext_signed_cert_timestamp_list, + &ret->tlsext_signed_cert_timestamp_list_length, + kSignedCertTimestampListTag) || + !d2i_SSL_SESSION_get_octet_string( + &session, &ret->ocsp_response, &ret->ocsp_response_length, + kOCSPResponseTag)) { + goto err; + } + if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret, + kExtendedMasterSecretTag, + 0 /* default to false */)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->extended_master_secret = extended_master_secret; + + /* Ignore |version|. The structure version number is ignored. */ + + /* Only support SSLv3/TLS and DTLS. */ + if ((ssl_version >> 8) != SSL3_VERSION_MAJOR && + (ssl_version >> 8) != (DTLS1_VERSION >> 8)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_UNKNOWN_SSL_VERSION); + goto err; + } + ret->ssl_version = ssl_version; + + uint16_t cipher_value; + if (!CBS_get_u16(&cipher, &cipher_value) || CBS_len(&cipher) != 0) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_CIPHER_CODE_WRONG_LENGTH); + goto err; + } + ret->cipher = ssl3_get_cipher_by_value(cipher_value); + if (ret->cipher == NULL) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_UNSUPPORTED_CIPHER); + goto err; + } + + if (CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + memcpy(ret->session_id, CBS_data(&session_id), CBS_len(&session_id)); + ret->session_id_length = CBS_len(&session_id); + + if (CBS_len(&master_key) > SSL_MAX_MASTER_KEY_LENGTH) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key)); + ret->master_key_length = CBS_len(&master_key); + + if (session_time > LONG_MAX || + timeout > LONG_MAX) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->time = session_time; + ret->timeout = timeout; + + if (ret->peer != NULL) { + X509_free(ret->peer); + ret->peer = NULL; + } + if (has_peer) { + const uint8_t *ptr; + ptr = CBS_data(&peer); + ret->peer = d2i_X509(NULL, &ptr, CBS_len(&peer)); + if (ret->peer == NULL) { + goto err; + } + if (ptr != CBS_data(&peer) + CBS_len(&peer)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + } + + if (CBS_len(&sid_ctx) > sizeof(ret->sid_ctx)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + memcpy(ret->sid_ctx, CBS_data(&sid_ctx), CBS_len(&sid_ctx)); + ret->sid_ctx_length = CBS_len(&sid_ctx); + + if (verify_result > LONG_MAX || + ticket_lifetime_hint > 0xffffffff) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + ret->verify_result = verify_result; + ret->tlsext_tick_lifetime_hint = ticket_lifetime_hint; + + if (has_peer_sha256) { + if (CBS_len(&peer_sha256) != sizeof(ret->peer_sha256)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + memcpy(ret->peer_sha256, CBS_data(&peer_sha256), sizeof(ret->peer_sha256)); + ret->peer_sha256_valid = 1; + } else { + ret->peer_sha256_valid = 0; + } + + if (CBS_len(&original_handshake_hash) > + sizeof(ret->original_handshake_hash)) { + OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION); + goto err; + } + memcpy(ret->original_handshake_hash, CBS_data(&original_handshake_hash), + CBS_len(&original_handshake_hash)); + ret->original_handshake_hash_len = CBS_len(&original_handshake_hash); + + if (a) { + *a = ret; + } + *pp = CBS_data(&cbs); + return ret; + +err: + if (a && *a != ret) { + SSL_SESSION_free(ret); + } + return NULL; +} diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c new file mode 100644 index 0000000..624c41a --- /dev/null +++ b/src/ssl/ssl_cert.c @@ -0,0 +1,1081 @@ +/* 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 <stdio.h> + +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/buf.h> +#include <openssl/dh.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/pem.h> +#include <openssl/x509v3.h> + +#include "../crypto/dh/internal.h" +#include "../crypto/directory.h" +#include "ssl_locl.h" + + +int SSL_get_ex_data_X509_STORE_CTX_idx(void) { + static int ssl_x509_store_ctx_idx = -1; + int got_write_lock = 0; + + CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); + + if (ssl_x509_store_ctx_idx < 0) { + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + got_write_lock = 1; + + if (ssl_x509_store_ctx_idx < 0) { + ssl_x509_store_ctx_idx = X509_STORE_CTX_get_ex_new_index( + 0, "SSL for verify callback", NULL, NULL, NULL); + } + } + + if (got_write_lock) { + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + } else { + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); + } + + return ssl_x509_store_ctx_idx; +} + +CERT *ssl_cert_new(void) { + CERT *ret; + + ret = (CERT *)OPENSSL_malloc(sizeof(CERT)); + if (ret == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_new, ERR_R_MALLOC_FAILURE); + return NULL; + } + memset(ret, 0, sizeof(CERT)); + + ret->key = &ret->pkeys[SSL_PKEY_RSA_ENC]; + return ret; +} + +CERT *ssl_cert_dup(CERT *cert) { + CERT *ret; + int i; + + ret = (CERT *)OPENSSL_malloc(sizeof(CERT)); + if (ret == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_MALLOC_FAILURE); + return NULL; + } + + memset(ret, 0, sizeof(CERT)); + + ret->key = &ret->pkeys[cert->key - &cert->pkeys[0]]; + /* or ret->key = ret->pkeys + (cert->key - cert->pkeys), if you find that + * more readable */ + + ret->mask_k = cert->mask_k; + ret->mask_a = cert->mask_a; + + if (cert->dh_tmp != NULL) { + ret->dh_tmp = DHparams_dup(cert->dh_tmp); + if (ret->dh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_DH_LIB); + goto err; + } + if (cert->dh_tmp->priv_key) { + BIGNUM *b = BN_dup(cert->dh_tmp->priv_key); + if (!b) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_BN_LIB); + goto err; + } + ret->dh_tmp->priv_key = b; + } + if (cert->dh_tmp->pub_key) { + BIGNUM *b = BN_dup(cert->dh_tmp->pub_key); + if (!b) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_BN_LIB); + goto err; + } + ret->dh_tmp->pub_key = b; + } + } + ret->dh_tmp_cb = cert->dh_tmp_cb; + + if (cert->ecdh_tmp) { + ret->ecdh_tmp = EC_KEY_dup(cert->ecdh_tmp); + if (ret->ecdh_tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_EC_LIB); + goto err; + } + } + ret->ecdh_tmp_cb = cert->ecdh_tmp_cb; + ret->ecdh_tmp_auto = cert->ecdh_tmp_auto; + + for (i = 0; i < SSL_PKEY_NUM; i++) { + CERT_PKEY *cpk = cert->pkeys + i; + CERT_PKEY *rpk = ret->pkeys + i; + if (cpk->x509 != NULL) { + rpk->x509 = X509_up_ref(cpk->x509); + } + + if (cpk->privatekey != NULL) { + rpk->privatekey = EVP_PKEY_dup(cpk->privatekey); + } + + if (cpk->chain) { + rpk->chain = X509_chain_up_ref(cpk->chain); + if (!rpk->chain) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_dup, ERR_R_MALLOC_FAILURE); + goto err; + } + } + } + + /* Peer sigalgs set to NULL as we get these from handshake too */ + ret->peer_sigalgs = NULL; + ret->peer_sigalgslen = 0; + /* Configured sigalgs however we copy across */ + + if (cert->conf_sigalgs) { + ret->conf_sigalgs = OPENSSL_malloc(cert->conf_sigalgslen); + if (!ret->conf_sigalgs) { + goto err; + } + memcpy(ret->conf_sigalgs, cert->conf_sigalgs, cert->conf_sigalgslen); + ret->conf_sigalgslen = cert->conf_sigalgslen; + } else { + ret->conf_sigalgs = NULL; + } + + if (cert->client_sigalgs) { + ret->client_sigalgs = OPENSSL_malloc(cert->client_sigalgslen); + if (!ret->client_sigalgs) { + goto err; + } + memcpy(ret->client_sigalgs, cert->client_sigalgs, cert->client_sigalgslen); + ret->client_sigalgslen = cert->client_sigalgslen; + } else { + ret->client_sigalgs = NULL; + } + /* Shared sigalgs also NULL */ + ret->shared_sigalgs = NULL; + /* Copy any custom client certificate types */ + if (cert->client_certificate_types) { + ret->client_certificate_types = BUF_memdup( + cert->client_certificate_types, cert->num_client_certificate_types); + if (!ret->client_certificate_types) { + goto err; + } + ret->num_client_certificate_types = cert->num_client_certificate_types; + } + + ret->cert_flags = cert->cert_flags; + + ret->cert_cb = cert->cert_cb; + ret->cert_cb_arg = cert->cert_cb_arg; + + if (cert->verify_store) { + CRYPTO_add(&cert->verify_store->references, 1, CRYPTO_LOCK_X509_STORE); + ret->verify_store = cert->verify_store; + } + + if (cert->chain_store) { + CRYPTO_add(&cert->chain_store->references, 1, CRYPTO_LOCK_X509_STORE); + ret->chain_store = cert->chain_store; + } + + ret->ciphers_raw = NULL; + + return ret; + +err: + ssl_cert_free(ret); + return NULL; +} + +/* Free up and clear all certificates and chains */ +void ssl_cert_clear_certs(CERT *c) { + int i; + if (c == NULL) { + return; + } + + for (i = 0; i < SSL_PKEY_NUM; i++) { + CERT_PKEY *cpk = c->pkeys + i; + if (cpk->x509) { + X509_free(cpk->x509); + cpk->x509 = NULL; + } + if (cpk->privatekey) { + EVP_PKEY_free(cpk->privatekey); + cpk->privatekey = NULL; + } + if (cpk->chain) { + sk_X509_pop_free(cpk->chain, X509_free); + cpk->chain = NULL; + } + } +} + +void ssl_cert_free(CERT *c) { + if (c == NULL) { + return; + } + + if (c->dh_tmp) { + DH_free(c->dh_tmp); + } + if (c->ecdh_tmp) { + EC_KEY_free(c->ecdh_tmp); + } + + ssl_cert_clear_certs(c); + if (c->peer_sigalgs) { + OPENSSL_free(c->peer_sigalgs); + } + if (c->conf_sigalgs) { + OPENSSL_free(c->conf_sigalgs); + } + if (c->client_sigalgs) { + OPENSSL_free(c->client_sigalgs); + } + if (c->shared_sigalgs) { + OPENSSL_free(c->shared_sigalgs); + } + if (c->client_certificate_types) { + OPENSSL_free(c->client_certificate_types); + } + if (c->verify_store) { + X509_STORE_free(c->verify_store); + } + if (c->chain_store) { + X509_STORE_free(c->chain_store); + } + if (c->ciphers_raw) { + OPENSSL_free(c->ciphers_raw); + } + + OPENSSL_free(c); +} + +int ssl_cert_inst(CERT **o) { + /* Create a CERT if there isn't already one (which cannot really happen, as + * it is initially created in SSL_CTX_new; but the earlier code usually + * allows for that one being non-existant, so we follow that behaviour, as it + * might turn out that there actually is a reason for it -- but I'm not sure + * that *all* of the existing code could cope with s->cert being NULL, + * otherwise we could do without the initialization in SSL_CTX_new). */ + + if (o == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_inst, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (*o == NULL) { + *o = ssl_cert_new(); + if (*o == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_cert_new, ERR_R_MALLOC_FAILURE); + return 0; + } + } + + return 1; +} + +int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) * chain) { + CERT_PKEY *cpk = c->key; + if (!cpk) { + return 0; + } + if (cpk->chain) { + sk_X509_pop_free(cpk->chain, X509_free); + } + cpk->chain = chain; + return 1; +} + +int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) * chain) { + STACK_OF(X509) * dchain; + if (!chain) { + return ssl_cert_set0_chain(c, NULL); + } + + dchain = X509_chain_up_ref(chain); + if (!dchain) { + return 0; + } + + if (!ssl_cert_set0_chain(c, dchain)) { + sk_X509_pop_free(dchain, X509_free); + return 0; + } + + return 1; +} + +int ssl_cert_add0_chain_cert(CERT *c, X509 *x) { + CERT_PKEY *cpk = c->key; + if (!cpk) { + return 0; + } + + if (!cpk->chain) { + cpk->chain = sk_X509_new_null(); + } + if (!cpk->chain || !sk_X509_push(cpk->chain, x)) { + return 0; + } + + return 1; +} + +int ssl_cert_add1_chain_cert(CERT *c, X509 *x) { + if (!ssl_cert_add0_chain_cert(c, x)) { + return 0; + } + + X509_up_ref(x); + return 1; +} + +int ssl_cert_select_current(CERT *c, X509 *x) { + int i; + if (x == NULL) { + return 0; + } + + for (i = 0; i < SSL_PKEY_NUM; i++) { + if (c->pkeys[i].x509 == x) { + c->key = &c->pkeys[i]; + return 1; + } + } + + for (i = 0; i < SSL_PKEY_NUM; i++) { + if (c->pkeys[i].x509 && !X509_cmp(c->pkeys[i].x509, x)) { + c->key = &c->pkeys[i]; + return 1; + } + } + + return 0; +} + +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; +} + +SESS_CERT *ssl_sess_cert_new(void) { + SESS_CERT *ret; + + ret = OPENSSL_malloc(sizeof *ret); + if (ret == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_sess_cert_new, ERR_R_MALLOC_FAILURE); + return NULL; + } + + memset(ret, 0, sizeof *ret); + ret->peer_key = &(ret->peer_pkeys[SSL_PKEY_RSA_ENC]); + + return ret; +} + +void ssl_sess_cert_free(SESS_CERT *sc) { + int i; + + if (sc == NULL) { + return; + } + + if (sc->cert_chain != NULL) { + sk_X509_pop_free(sc->cert_chain, X509_free); + } + + for (i = 0; i < SSL_PKEY_NUM; i++) { + if (sc->peer_pkeys[i].x509 != NULL) { + X509_free(sc->peer_pkeys[i].x509); + } + } + + if (sc->peer_dh_tmp != NULL) { + DH_free(sc->peer_dh_tmp); + } + if (sc->peer_ecdh_tmp != NULL) { + EC_KEY_free(sc->peer_ecdh_tmp); + } + + OPENSSL_free(sc); +} + +int ssl_set_peer_cert_type(SESS_CERT *sc, int type) { + sc->peer_cert_type = type; + return 1; +} + +int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) * sk) { + X509 *x; + int i; + X509_STORE *verify_store; + X509_STORE_CTX ctx; + + if (s->cert->verify_store) { + verify_store = s->cert->verify_store; + } else { + verify_store = s->ctx->cert_store; + } + + if (sk == NULL || sk_X509_num(sk) == 0) { + return 0; + } + + x = sk_X509_value(sk, 0); + if (!X509_STORE_CTX_init(&ctx, verify_store, x, sk)) { + OPENSSL_PUT_ERROR(SSL, ssl_verify_cert_chain, ERR_R_X509_LIB); + return 0; + } + X509_STORE_CTX_set_ex_data(&ctx, SSL_get_ex_data_X509_STORE_CTX_idx(), s); + + /* 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, s->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), s->param); + + if (s->verify_callback) { + X509_STORE_CTX_set_verify_cb(&ctx, s->verify_callback); + } + + if (s->ctx->app_verify_callback != NULL) { + i = s->ctx->app_verify_callback(&ctx, s->ctx->app_verify_arg); + } else { + i = X509_verify_cert(&ctx); + } + + s->verify_result = ctx.error; + X509_STORE_CTX_cleanup(&ctx); + + return i; +} + +static void set_client_CA_list(STACK_OF(X509_NAME) * *ca_list, + STACK_OF(X509_NAME) * name_list) { + if (*ca_list != NULL) { + sk_X509_NAME_pop_free(*ca_list, X509_NAME_free); + } + + *ca_list = name_list; +} + +STACK_OF(X509_NAME) * SSL_dup_CA_list(STACK_OF(X509_NAME) * sk) { + size_t i; + STACK_OF(X509_NAME) * ret; + X509_NAME *name; + + ret = sk_X509_NAME_new_null(); + for (i = 0; i < sk_X509_NAME_num(sk); i++) { + name = X509_NAME_dup(sk_X509_NAME_value(sk, i)); + if (name == NULL || !sk_X509_NAME_push(ret, name)) { + sk_X509_NAME_pop_free(ret, X509_NAME_free); + return NULL; + } + } + + return ret; +} + +void SSL_set_client_CA_list(SSL *s, STACK_OF(X509_NAME) * name_list) { + set_client_CA_list(&(s->client_CA), name_list); +} + +void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, STACK_OF(X509_NAME) * name_list) { + set_client_CA_list(&(ctx->client_CA), name_list); +} + +STACK_OF(X509_NAME) * SSL_CTX_get_client_CA_list(const SSL_CTX *ctx) { + return ctx->client_CA; +} + +STACK_OF(X509_NAME) * SSL_get_client_CA_list(const SSL *s) { + if (s->server) { + if (s->client_CA != NULL) { + return s->client_CA; + } else { + return s->ctx->client_CA; + } + } else { + if ((s->version >> 8) == SSL3_VERSION_MAJOR && s->s3 != NULL) { + return s->s3->tmp.ca_names; + } else { + return NULL; + } + } +} + +static int add_client_CA(STACK_OF(X509_NAME) * *sk, X509 *x) { + X509_NAME *name; + + if (x == NULL) { + return 0; + } + if (*sk == NULL) { + *sk = sk_X509_NAME_new_null(); + if (*sk == NULL) { + return 0; + } + } + + name = X509_NAME_dup(X509_get_subject_name(x)); + if (name == NULL) { + return 0; + } + + if (!sk_X509_NAME_push(*sk, name)) { + X509_NAME_free(name); + return 0; + } + + return 1; +} + +int SSL_add_client_CA(SSL *ssl, X509 *x) { + return add_client_CA(&(ssl->client_CA), x); +} + +int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) { + return add_client_CA(&(ctx->client_CA), x); +} + +static int xname_cmp(const X509_NAME **a, const X509_NAME **b) { + return X509_NAME_cmp(*a, *b); +} + +/* Load CA certs from a file into a STACK. Note that it is somewhat misnamed; + * it doesn't really have anything to do with clients (except that a common use + * for a stack of CAs is to send it to the client). Actually, it doesn't have + * much to do with CAs, either, since it will load any old cert. + * + * \param file the file containing one or more certs. + * \return a ::STACK containing the certs. */ +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, SSL_load_client_CA_file, 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, SSL_load_client_CA_file, ERR_R_MALLOC_FAILURE); + goto err; + } + } + xn = X509_get_subject_name(x); + if (xn == NULL) { + goto err; + } + + /* check for duplicates */ + xn = X509_NAME_dup(xn); + if (xn == NULL) { + goto err; + } + if (sk_X509_NAME_find(sk, NULL, xn)) { + X509_NAME_free(xn); + } else { + sk_X509_NAME_push(sk, xn); + sk_X509_NAME_push(ret, xn); + } + } + + if (0) { + err: + if (ret != NULL) { + sk_X509_NAME_pop_free(ret, X509_NAME_free); + } + ret = NULL; + } + + if (sk != NULL) { + sk_X509_NAME_free(sk); + } + if (in != NULL) { + BIO_free(in); + } + if (x != NULL) { + X509_free(x); + } + if (ret != NULL) { + ERR_clear_error(); + } + return ret; +} + +/* Add a file of certs to a stack. + * + * \param stack the stack to add to. + * \param file the file to add from. All certs in this file that are not + * already in the stack will be added. + * \return 1 for success, 0 for failure. Note that in the case of failure some + * certs may have been added to \c stack. */ +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 = 1; + 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, SSL_add_file_cert_subjects_to_stack, + 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; + } + xn = X509_NAME_dup(xn); + if (xn == NULL) { + goto err; + } + if (sk_X509_NAME_find(stack, NULL, xn)) { + X509_NAME_free(xn); + } else { + sk_X509_NAME_push(stack, xn); + } + } + + ERR_clear_error(); + + if (0) { + err: + ret = 0; + } + + if (in != NULL) { + BIO_free(in); + } + if (x != NULL) { + X509_free(x); + } + + (void) sk_X509_NAME_set_cmp_func(stack, oldcmp); + + return ret; +} + +/* Add a directory of certs to a stack. + * + * \param stack the stack to append to. + * \param dir the directory to append from. All files in this directory will be + * examined as potential certs. Any that are acceptable to + * SSL_add_dir_cert_subjects_to_stack() that are not already in the stack will + * be included. + * \return 1 for success, 0 for failure. Note that in the case of failure some + * certs may have been added to \c stack. */ +int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) * stack, + const char *dir) { + OPENSSL_DIR_CTX *d = NULL; + const char *filename; + int ret = 0; + + CRYPTO_w_lock(CRYPTO_LOCK_READDIR); + + /* Note that a side effect is that the CAs will be sorted by name */ + while ((filename = OPENSSL_DIR_read(&d, dir))) { + char buf[1024]; + int r; + + if (strlen(dir) + strlen(filename) + 2 > sizeof(buf)) { + OPENSSL_PUT_ERROR(SSL, SSL_add_dir_cert_subjects_to_stack, + SSL_R_PATH_TOO_LONG); + goto err; + } + + r = BIO_snprintf(buf, sizeof buf, "%s/%s", dir, filename); + if (r <= 0 || r >= (int)sizeof(buf) || + !SSL_add_file_cert_subjects_to_stack(stack, buf)) { + goto err; + } + } + + if (errno) { + OPENSSL_PUT_ERROR(SSL, SSL_add_file_cert_subjects_to_stack, ERR_R_SYS_LIB); + ERR_add_error_data(3, "OPENSSL_DIR_read(&ctx, '", dir, "')"); + goto err; + } + + ret = 1; + +err: + if (d) { + OPENSSL_DIR_end(&d); + } + CRYPTO_w_unlock(CRYPTO_LOCK_READDIR); + return ret; +} + +/* Add a certificate to a BUF_MEM structure */ +static int ssl_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x) { + int n; + uint8_t *p; + + n = i2d_X509(x, NULL); + if (!BUF_MEM_grow_clean(buf, (int)(n + (*l) + 3))) { + OPENSSL_PUT_ERROR(SSL, ssl_add_cert_to_buf, ERR_R_BUF_LIB); + return 0; + } + p = (uint8_t *)&(buf->data[*l]); + l2n3(n, p); + i2d_X509(x, &p); + *l += n + 3; + + return 1; +} + +/* Add certificate chain to internal SSL BUF_MEM structure. */ +int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l) { + BUF_MEM *buf = s->init_buf; + int no_chain = 0; + size_t i; + + X509 *x = NULL; + STACK_OF(X509) * extra_certs; + X509_STORE *chain_store; + + if (cpk) { + x = cpk->x509; + } + + if (s->cert->chain_store) { + chain_store = s->cert->chain_store; + } else { + chain_store = s->ctx->cert_store; + } + + /* If we have a certificate specific chain use it, else use parent ctx. */ + if (cpk && cpk->chain) { + extra_certs = cpk->chain; + } else { + extra_certs = s->ctx->extra_certs; + } + + if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs) { + no_chain = 1; + } + + /* TLSv1 sends a chain with nothing in it, instead of an alert. */ + if (!BUF_MEM_grow_clean(buf, 10)) { + OPENSSL_PUT_ERROR(SSL, ssl_add_cert_chain, ERR_R_BUF_LIB); + return 0; + } + + if (x != NULL) { + if (no_chain) { + if (!ssl_add_cert_to_buf(buf, l, x)) { + return 0; + } + } else { + X509_STORE_CTX xs_ctx; + + if (!X509_STORE_CTX_init(&xs_ctx, chain_store, x, NULL)) { + OPENSSL_PUT_ERROR(SSL, ssl_add_cert_chain, ERR_R_X509_LIB); + return 0; + } + X509_verify_cert(&xs_ctx); + /* Don't leave errors in the queue */ + ERR_clear_error(); + for (i = 0; i < sk_X509_num(xs_ctx.chain); i++) { + x = sk_X509_value(xs_ctx.chain, i); + + if (!ssl_add_cert_to_buf(buf, l, x)) { + X509_STORE_CTX_cleanup(&xs_ctx); + return 0; + } + } + X509_STORE_CTX_cleanup(&xs_ctx); + } + } + + for (i = 0; i < sk_X509_num(extra_certs); i++) { + x = sk_X509_value(extra_certs, i); + if (!ssl_add_cert_to_buf(buf, l, x)) { + return 0; + } + } + + return 1; +} + +/* Build a certificate chain for current certificate */ +int ssl_build_cert_chain(CERT *c, X509_STORE *chain_store, int flags) { + CERT_PKEY *cpk = c->key; + X509_STORE_CTX xs_ctx; + STACK_OF(X509) *chain = NULL, *untrusted = NULL; + X509 *x; + int i, rv = 0; + unsigned long error; + + if (!cpk->x509) { + OPENSSL_PUT_ERROR(SSL, ssl_build_cert_chain, SSL_R_NO_CERTIFICATE_SET); + goto err; + } + + /* Rearranging and check the chain: add everything to a store */ + if (flags & SSL_BUILD_CHAIN_FLAG_CHECK) { + size_t j; + chain_store = X509_STORE_new(); + if (!chain_store) { + goto err; + } + + for (j = 0; j < sk_X509_num(cpk->chain); j++) { + x = sk_X509_value(cpk->chain, j); + if (!X509_STORE_add_cert(chain_store, x)) { + error = ERR_peek_last_error(); + if (ERR_GET_LIB(error) != ERR_LIB_X509 || + ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { + goto err; + } + ERR_clear_error(); + } + } + + /* Add EE cert too: it might be self signed */ + if (!X509_STORE_add_cert(chain_store, cpk->x509)) { + error = ERR_peek_last_error(); + if (ERR_GET_LIB(error) != ERR_LIB_X509 || + ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { + goto err; + } + ERR_clear_error(); + } + } else { + if (c->chain_store) { + chain_store = c->chain_store; + } + + if (flags & SSL_BUILD_CHAIN_FLAG_UNTRUSTED) { + untrusted = cpk->chain; + } + } + + if (!X509_STORE_CTX_init(&xs_ctx, chain_store, cpk->x509, untrusted)) { + OPENSSL_PUT_ERROR(SSL, ssl_build_cert_chain, ERR_R_X509_LIB); + goto err; + } + + i = X509_verify_cert(&xs_ctx); + if (i <= 0 && flags & SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR) { + if (flags & SSL_BUILD_CHAIN_FLAG_CLEAR_ERROR) { + ERR_clear_error(); + } + i = 1; + rv = 2; + } + + if (i > 0) { + chain = X509_STORE_CTX_get1_chain(&xs_ctx); + } + if (i <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl_build_cert_chain, + SSL_R_CERTIFICATE_VERIFY_FAILED); + i = X509_STORE_CTX_get_error(&xs_ctx); + ERR_add_error_data(2, "Verify error:", X509_verify_cert_error_string(i)); + + X509_STORE_CTX_cleanup(&xs_ctx); + goto err; + } + + X509_STORE_CTX_cleanup(&xs_ctx); + if (cpk->chain) { + sk_X509_pop_free(cpk->chain, X509_free); + } + + /* Remove EE certificate from chain */ + x = sk_X509_shift(chain); + X509_free(x); + if (flags & SSL_BUILD_CHAIN_FLAG_NO_ROOT) { + if (sk_X509_num(chain) > 0) { + /* See if last cert is self signed */ + x = sk_X509_value(chain, sk_X509_num(chain) - 1); + X509_check_purpose(x, -1, 0); + if (x->ex_flags & EXFLAG_SS) { + x = sk_X509_pop(chain); + X509_free(x); + } + } + } + + cpk->chain = chain; + if (rv == 0) + rv = 1; + +err: + if (flags & SSL_BUILD_CHAIN_FLAG_CHECK) { + X509_STORE_free(chain_store); + } + + return rv; +} + +int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain, int ref) { + X509_STORE **pstore; + if (chain) { + pstore = &c->chain_store; + } else { + pstore = &c->verify_store; + } + + if (*pstore) { + X509_STORE_free(*pstore); + } + *pstore = store; + + if (ref && store) { + CRYPTO_add(&store->references, 1, CRYPTO_LOCK_X509_STORE); + } + return 1; +} diff --git a/src/ssl/ssl_ciph.c b/src/ssl/ssl_ciph.c new file mode 100644 index 0000000..60b9747 --- /dev/null +++ b/src/ssl/ssl_ciph.c @@ -0,0 +1,1421 @@ +/* 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 <stdio.h> +#include <assert.h> + +#include <openssl/engine.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/sha.h> + +#include "ssl_locl.h" + + +struct handshake_digest { + long mask; + const EVP_MD *(*md_func)(void); +}; + +static const struct handshake_digest ssl_handshake_digests[SSL_MAX_DIGEST] = { + {SSL_HANDSHAKE_MAC_MD5, EVP_md5}, + {SSL_HANDSHAKE_MAC_SHA, EVP_sha1}, + {SSL_HANDSHAKE_MAC_SHA256, EVP_sha256}, + {SSL_HANDSHAKE_MAC_SHA384, EVP_sha384}, +}; + +#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 dead; + int in_group; + struct cipher_order_st *next, *prev; +} CIPHER_ORDER; + +static const SSL_CIPHER cipher_aliases[] = + { + {0, SSL_TXT_ALL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* "COMPLEMENTOFDEFAULT" (does *not* include ciphersuites not found in + ALL!) */ + {0, SSL_TXT_CMPDEF, 0, SSL_kEDH | SSL_kEECDH, SSL_aNULL, 0, 0, 0, 0, 0, 0, + 0}, + + /* 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) */ + {0, SSL_TXT_kRSA, 0, SSL_kRSA, 0, 0, 0, 0, 0, 0, 0, 0}, + + {0, SSL_TXT_kEDH, 0, SSL_kEDH, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_DH, 0, SSL_kEDH, 0, 0, 0, 0, 0, 0, 0, 0}, + + {0, SSL_TXT_kEECDH, 0, SSL_kEECDH, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_ECDH, 0, SSL_kEECDH, 0, 0, 0, 0, 0, 0, 0, 0}, + + {0, SSL_TXT_kPSK, 0, SSL_kPSK, 0, 0, 0, 0, 0, 0, 0, 0}, + + /* server authentication aliases */ + {0, SSL_TXT_aRSA, 0, 0, SSL_aRSA, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_aNULL, 0, 0, SSL_aNULL, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_aECDSA, 0, 0, SSL_aECDSA, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_ECDSA, 0, 0, SSL_aECDSA, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_aPSK, 0, 0, SSL_aPSK, 0, 0, 0, 0, 0, 0, 0}, + + /* aliases combining key exchange and server authentication */ + {0, SSL_TXT_EDH, 0, SSL_kEDH, ~SSL_aNULL, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_EECDH, 0, SSL_kEECDH, ~SSL_aNULL, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_RSA, 0, SSL_kRSA, SSL_aRSA, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_ADH, 0, SSL_kEDH, SSL_aNULL, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_AECDH, 0, SSL_kEECDH, SSL_aNULL, 0, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_PSK, 0, SSL_kPSK, SSL_aPSK, 0, 0, 0, 0, 0, 0, 0}, + + /* symmetric encryption aliases */ + {0, SSL_TXT_3DES, 0, 0, 0, SSL_3DES, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_RC4, 0, 0, 0, SSL_RC4, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_AES128, 0, 0, 0, SSL_AES128 | SSL_AES128GCM, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_AES256, 0, 0, 0, SSL_AES256 | SSL_AES256GCM, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_AES, 0, 0, 0, SSL_AES, 0, 0, 0, 0, 0, 0}, + {0, SSL_TXT_AES_GCM, 0, 0, 0, SSL_AES128GCM | SSL_AES256GCM, 0, 0, 0, 0, 0, + 0}, + {0, SSL_TXT_CHACHA20, 0, 0, 0, SSL_CHACHA20POLY1305, 0, 0, 0, 0, 0, 0}, + + /* MAC aliases */ + {0, SSL_TXT_MD5, 0, 0, 0, 0, SSL_MD5, 0, 0, 0, 0, 0}, + {0, SSL_TXT_SHA1, 0, 0, 0, 0, SSL_SHA1, 0, 0, 0, 0, 0}, + {0, SSL_TXT_SHA, 0, 0, 0, 0, SSL_SHA1, 0, 0, 0, 0, 0}, + {0, SSL_TXT_SHA256, 0, 0, 0, 0, SSL_SHA256, 0, 0, 0, 0, 0}, + {0, SSL_TXT_SHA384, 0, 0, 0, 0, SSL_SHA384, 0, 0, 0, 0, 0}, + + /* protocol version aliases */ + {0, SSL_TXT_SSLV3, 0, 0, 0, 0, 0, SSL_SSLV3, 0, 0, 0, 0}, + {0, SSL_TXT_TLSV1, 0, 0, 0, 0, 0, SSL_TLSV1, 0, 0, 0, 0}, + {0, SSL_TXT_TLSV1_2, 0, 0, 0, 0, 0, SSL_TLSV1_2, 0, 0, 0, 0}, + + /* strength classes */ + {0, SSL_TXT_MEDIUM, 0, 0, 0, 0, 0, 0, SSL_MEDIUM, 0, 0, 0}, + {0, SSL_TXT_HIGH, 0, 0, 0, 0, 0, 0, SSL_HIGH, 0, 0, 0}, + /* FIPS 140-2 approved ciphersuite */ + {0, SSL_TXT_FIPS, 0, 0, 0, 0, 0, 0, SSL_FIPS, 0, 0, 0}, +}; + +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; + + switch (cipher->algorithm_enc) { + case SSL_AES128GCM: + *out_aead = EVP_aead_aes_128_gcm(); + *out_fixed_iv_len = 4; + return 1; + + case SSL_AES256GCM: + *out_aead = EVP_aead_aes_256_gcm(); + *out_fixed_iv_len = 4; + return 1; + + case SSL_CHACHA20POLY1305: + *out_aead = EVP_aead_chacha20_poly1305(); + *out_fixed_iv_len = 0; + return 1; + + case SSL_RC4: + switch (cipher->algorithm_mac) { + case SSL_MD5: + if (version == SSL3_VERSION) { + *out_aead = EVP_aead_rc4_md5_ssl3(); + } else { + *out_aead = EVP_aead_rc4_md5_tls(); + } + *out_mac_secret_len = MD5_DIGEST_LENGTH; + return 1; + case SSL_SHA1: + if (version == SSL3_VERSION) { + *out_aead = EVP_aead_rc4_sha1_ssl3(); + } else { + *out_aead = EVP_aead_rc4_sha1_tls(); + } + *out_mac_secret_len = SHA_DIGEST_LENGTH; + return 1; + default: + return 0; + } + + case SSL_AES128: + switch (cipher->algorithm_mac) { + case SSL_SHA1: + 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(); + } + *out_mac_secret_len = SHA_DIGEST_LENGTH; + return 1; + case SSL_SHA256: + *out_aead = EVP_aead_aes_128_cbc_sha256_tls(); + *out_mac_secret_len = SHA256_DIGEST_LENGTH; + return 1; + default: + return 0; + } + + case SSL_AES256: + switch (cipher->algorithm_mac) { + case SSL_SHA1: + 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(); + } + *out_mac_secret_len = SHA_DIGEST_LENGTH; + return 1; + case SSL_SHA256: + *out_aead = EVP_aead_aes_256_cbc_sha256_tls(); + *out_mac_secret_len = SHA256_DIGEST_LENGTH; + return 1; + case SSL_SHA384: + *out_aead = EVP_aead_aes_256_cbc_sha384_tls(); + *out_mac_secret_len = SHA384_DIGEST_LENGTH; + return 1; + default: + return 0; + } + + case SSL_3DES: + switch (cipher->algorithm_mac) { + case SSL_SHA1: + 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(); + } + *out_mac_secret_len = SHA_DIGEST_LENGTH; + return 1; + default: + return 0; + } + + default: + return 0; + } +} + +int ssl_get_handshake_digest(size_t idx, long *mask, const EVP_MD **md) { + if (idx >= SSL_MAX_DIGEST) { + return 0; + } + *mask = ssl_handshake_digests[idx].mask; + *md = ssl_handshake_digests[idx].md_func(); + return 1; +} + +#define ITEM_SEP(a) \ + (((a) == ':') || ((a) == ' ') || ((a) == ';') || ((a) == ',')) + +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, + int num_of_ciphers, + CIPHER_ORDER *co_list, + CIPHER_ORDER **head_p, + CIPHER_ORDER **tail_p) { + int i, co_list_num; + const SSL_CIPHER *c; + + /* We have num_of_ciphers descriptions compiled in, depending on the method + * selected (SSLv2 and/or SSLv3, TLSv1 etc). These will later be sorted in a + * linked list with at most num entries. */ + + /* Get the initial list of ciphers */ + co_list_num = 0; /* actual count of ciphers */ + for (i = 0; i < num_of_ciphers; i++) { + c = ssl_method->get_cipher(i); + /* drop those that use any of that is not available */ + if (c != NULL && c->valid) { + co_list[co_list_num].cipher = c; + 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 (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]; + } +} + +static void ssl_cipher_collect_aliases(const SSL_CIPHER **ca_list, + int num_of_group_aliases, + CIPHER_ORDER *head) { + CIPHER_ORDER *ciph_curr; + const SSL_CIPHER **ca_curr; + int i; + + /* First, add the real ciphers as already collected. */ + ciph_curr = head; + ca_curr = ca_list; + while (ciph_curr != NULL) { + *ca_curr = ciph_curr->cipher; + ca_curr++; + ciph_curr = ciph_curr->next; + } + + /* Now we add the available ones from the cipher_aliases[] table. They + * represent either one or more algorithms, some of which in any affected + * category must be supported (set in enabled_mask), or represent a cipher + * strength value (will be added in any case because algorithms=0). */ + for (i = 0; i < num_of_group_aliases; i++) { + *ca_curr = cipher_aliases + i; + ca_curr++; + } + + *ca_curr = NULL; /* end of list */ +} + +static void ssl_cipher_apply_rule( + unsigned long cipher_id, unsigned long alg_mkey, unsigned long alg_auth, + unsigned long alg_enc, unsigned long alg_mac, unsigned long alg_ssl, + unsigned long algo_strength, 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 (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 the value of strength_bits + * or the algorithms used. */ + if (strength_bits >= 0) { + if (strength_bits != cp->strength_bits) { + continue; + } + } else { + if ((alg_mkey && !(alg_mkey & cp->algorithm_mkey)) || + (alg_auth && !(alg_auth & cp->algorithm_auth)) || + (alg_enc && !(alg_enc & cp->algorithm_enc)) || + (alg_mac && !(alg_mac & cp->algorithm_mac)) || + (alg_ssl && !(alg_ssl & cp->algorithm_ssl)) || + (algo_strength && !(algo_strength & cp->algo_strength))) { + 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 && curr->cipher->strength_bits > max_strength_bits) { + max_strength_bits = curr->cipher->strength_bits; + } + curr = curr->next; + } + + number_uses = OPENSSL_malloc((max_strength_bits + 1) * sizeof(int)); + if (!number_uses) { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_strength_sort, ERR_R_MALLOC_FAILURE); + return 0; + } + 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[curr->cipher->strength_bits]++; + } + 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, 0, CIPHER_ORD, i, 0, head_p, + tail_p); + } + } + + OPENSSL_free(number_uses); + return 1; +} + +static int ssl_cipher_process_rulestr(const char *rule_str, + CIPHER_ORDER **head_p, + CIPHER_ORDER **tail_p, + const SSL_CIPHER **ca_list) { + unsigned long alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl, algo_strength; + const char *l, *buf; + int j, multi, found, rule, retval, ok, buflen, in_group = 0, has_group = 0; + unsigned long cipher_id = 0; + char ch; + + retval = 1; + l = rule_str; + for (;;) { + ch = *l; + + if (ch == '\0') { + break; /* done */ + } + + if (in_group) { + if (ch == ']') { + if (!in_group) { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, + SSL_R_UNEXPECTED_GROUP_CLOSE); + retval = found = in_group = 0; + break; + } + 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_cipher_process_rulestr, + SSL_R_UNEXPECTED_OPERATOR_IN_GROUP); + retval = found = in_group = 0; + break; + } 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_cipher_process_rulestr, SSL_R_NESTED_GROUP); + retval = found = in_group = 0; + break; + } + 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_cipher_process_rulestr, + SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS); + retval = found = in_group = 0; + break; + } + + if (ITEM_SEP(ch)) { + l++; + continue; + } + + alg_mkey = 0; + alg_auth = 0; + alg_enc = 0; + alg_mac = 0; + alg_ssl = 0; + algo_strength = 0; + + for (;;) { + ch = *l; + buf = l; + buflen = 0; + while (((ch >= 'A') && (ch <= 'Z')) || ((ch >= '0') && (ch <= '9')) || + ((ch >= 'a') && (ch <= 'z')) || (ch == '-') || (ch == '.')) { + ch = *(++l); + buflen++; + } + + if (buflen == 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_cipher_process_rulestr, + SSL_R_INVALID_COMMAND); + retval = found = in_group = 0; + l++; + break; + } + + if (rule == CIPHER_SPECIAL) { + found = 0; /* unused -- avoid compiler warning */ + break; /* special treatment */ + } + + /* check for multi-part specification */ + if (ch == '+') { + multi = 1; + l++; + } else { + multi = 0; + } + + /* Now search for the cipher alias in the ca_list. Be careful with the + * strncmp, because the "buflen" limitation will make the rule "ADH:SOME" + * and the cipher "ADH-MY-CIPHER" look like a match for buflen=3. So + * additionally check whether the cipher name found has the correct + * length. We can save a strlen() call: just checking for the '\0' at the + * right place is sufficient, we have to strncmp() anyway. (We cannot use + * strcmp(), because buf is not '\0' terminated.) */ + j = found = 0; + cipher_id = 0; + while (ca_list[j]) { + if (!strncmp(buf, ca_list[j]->name, buflen) && + (ca_list[j]->name[buflen] == '\0')) { + found = 1; + break; + } else { + j++; + } + } + + if (!found) { + break; /* ignore this entry */ + } + + if (ca_list[j]->algorithm_mkey) { + if (alg_mkey) { + alg_mkey &= ca_list[j]->algorithm_mkey; + if (!alg_mkey) { + found = 0; + break; + } + } else { + alg_mkey = ca_list[j]->algorithm_mkey; + } + } + + if (ca_list[j]->algorithm_auth) { + if (alg_auth) { + alg_auth &= ca_list[j]->algorithm_auth; + if (!alg_auth) { + found = 0; + break; + } + } else { + alg_auth = ca_list[j]->algorithm_auth; + } + } + + if (ca_list[j]->algorithm_enc) { + if (alg_enc) { + alg_enc &= ca_list[j]->algorithm_enc; + if (!alg_enc) { + found = 0; + break; + } + } else { + alg_enc = ca_list[j]->algorithm_enc; + } + } + + if (ca_list[j]->algorithm_mac) { + if (alg_mac) { + alg_mac &= ca_list[j]->algorithm_mac; + if (!alg_mac) { + found = 0; + break; + } + } else { + alg_mac = ca_list[j]->algorithm_mac; + } + } + + if (ca_list[j]->algo_strength) { + if (algo_strength) { + algo_strength &= ca_list[j]->algo_strength; + if (!algo_strength) { + found = 0; + break; + } + } else { + algo_strength |= ca_list[j]->algo_strength; + } + } + + if (ca_list[j]->valid) { + /* explicit ciphersuite found; its protocol version does not become + * part of the search pattern! */ + cipher_id = ca_list[j]->id; + } else { + /* not an explicit ciphersuite; only in this case, the protocol version + * is considered part of the search pattern. */ + if (ca_list[j]->algorithm_ssl) { + if (alg_ssl) { + alg_ssl &= ca_list[j]->algorithm_ssl; + if (!alg_ssl) { + found = 0; + break; + } + } else { + alg_ssl = ca_list[j]->algorithm_ssl; + } + } + } + + if (!multi) { + break; + } + } + + /* Ok, we have the rule, now apply it. */ + if (rule == CIPHER_SPECIAL) { + /* special command */ + ok = 0; + if (buflen == 8 && !strncmp(buf, "STRENGTH", 8)) { + ok = ssl_cipher_strength_sort(head_p, tail_p); + } else { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, + SSL_R_INVALID_COMMAND); + } + + if (ok == 0) { + retval = 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 (found) { + ssl_cipher_apply_rule(cipher_id, alg_mkey, alg_auth, alg_enc, alg_mac, + alg_ssl, algo_strength, rule, -1, in_group, head_p, + tail_p); + } else { + while (*l != '\0' && !ITEM_SEP(*l)) { + l++; + } + } + } + + if (in_group) { + OPENSSL_PUT_ERROR(SSL, ssl_cipher_process_rulestr, SSL_R_INVALID_COMMAND); + retval = 0; + } + + return retval; +} + +STACK_OF(SSL_CIPHER) * +ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method, + struct ssl_cipher_preference_list_st **cipher_list, + STACK_OF(SSL_CIPHER) * *cipher_list_by_id, + const char *rule_str, CERT *c) { + int ok, num_of_ciphers, num_of_alias_max, num_of_group_aliases; + STACK_OF(SSL_CIPHER) *cipherstack = NULL, *tmp_cipher_list = NULL; + const char *rule_p; + CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr; + const SSL_CIPHER **ca_list = NULL; + 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 || cipher_list == NULL) { + return NULL; + } + + /* 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. */ + num_of_ciphers = ssl_method->num_ciphers(); + co_list = + (CIPHER_ORDER *)OPENSSL_malloc(sizeof(CIPHER_ORDER) * num_of_ciphers); + if (co_list == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_create_cipher_list, ERR_R_MALLOC_FAILURE); + return NULL; + } + + ssl_cipher_collect_ciphers(ssl_method, num_of_ciphers, 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 then ECDHE_RSA over other + * key exchange mechanisms */ + ssl_cipher_apply_rule(0, SSL_kEECDH, SSL_aECDSA, 0, 0, 0, 0, CIPHER_ADD, -1, + 0, &head, &tail); + ssl_cipher_apply_rule(0, SSL_kEECDH, 0, 0, 0, 0, 0, CIPHER_ADD, -1, 0, &head, + &tail); + ssl_cipher_apply_rule(0, SSL_kEECDH, 0, 0, 0, 0, 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. */ + if (EVP_has_aes_hardware()) { + ssl_cipher_apply_rule(0, 0, 0, SSL_AES256GCM, 0, 0, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_AES128GCM, 0, 0, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_CHACHA20POLY1305, 0, 0, 0, CIPHER_ADD, + -1, 0, &head, &tail); + } else { + ssl_cipher_apply_rule(0, 0, 0, SSL_CHACHA20POLY1305, 0, 0, 0, CIPHER_ADD, + -1, 0, &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_AES256GCM, 0, 0, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_AES128GCM, 0, 0, 0, CIPHER_ADD, -1, 0, + &head, &tail); + } + + /* Then the legacy non-AEAD ciphers: AES_256_CBC, AES-128_CBC, RC4_128_SHA, + * RC4_128_MD5, 3DES_EDE_CBC_SHA. */ + ssl_cipher_apply_rule(0, 0, 0, SSL_AES256, 0, 0, 0, CIPHER_ADD, -1, 0, &head, + &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_AES128, 0, 0, 0, CIPHER_ADD, -1, 0, &head, + &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_RC4, ~SSL_MD5, 0, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_RC4, SSL_MD5, 0, 0, CIPHER_ADD, -1, 0, + &head, &tail); + ssl_cipher_apply_rule(0, 0, 0, SSL_3DES, 0, 0, 0, CIPHER_ADD, -1, 0, &head, + &tail); + + /* Temporarily enable everything else for sorting */ + ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_ADD, -1, 0, &head, &tail); + + /* Move ciphers without forward secrecy to the end. */ + ssl_cipher_apply_rule(0, ~(SSL_kEDH | SSL_kEECDH), 0, 0, 0, 0, 0, CIPHER_ORD, + -1, 0, &head, &tail); + + /* Move anonymous ciphers to the end. Usually, these will remain disabled. + * (For applications that allow them, they aren't too bad, but we prefer + * authenticated ciphers.) + * TODO(davidben): Remove them altogether? */ + ssl_cipher_apply_rule(0, 0, SSL_aNULL, 0, 0, 0, 0, CIPHER_ORD, -1, 0, &head, + &tail); + + /* Now disable everything (maintaining the ordering!) */ + ssl_cipher_apply_rule(0, 0, 0, 0, 0, 0, 0, CIPHER_DEL, -1, 0, &head, &tail); + + /* We also need cipher aliases for selecting based on the rule_str. There + * might be two types of entries in the rule_str: 1) names of ciphers + * themselves 2) aliases for groups of ciphers. For 1) we need the available + * ciphers and for 2) the cipher groups of cipher_aliases added together in + * one list (otherwise we would be happy with just the cipher_aliases + * table). */ + num_of_group_aliases = sizeof(cipher_aliases) / sizeof(SSL_CIPHER); + num_of_alias_max = num_of_ciphers + num_of_group_aliases + 1; + ca_list = OPENSSL_malloc(sizeof(SSL_CIPHER *) * num_of_alias_max); + if (ca_list == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_create_cipher_list, ERR_R_MALLOC_FAILURE); + goto err; + } + ssl_cipher_collect_aliases(ca_list, num_of_group_aliases, head); + + /* If the rule_string begins with DEFAULT, apply the default rule before + * using the (possibly available) additional rules. */ + ok = 1; + rule_p = rule_str; + if (strncmp(rule_str, "DEFAULT", 7) == 0) { + ok = ssl_cipher_process_rulestr(SSL_DEFAULT_CIPHER_LIST, &head, &tail, + ca_list); + rule_p += 7; + if (*rule_p == ':') { + rule_p++; + } + } + + if (ok && strlen(rule_p) > 0) { + ok = ssl_cipher_process_rulestr(rule_p, &head, &tail, ca_list); + } + + OPENSSL_free((void *)ca_list); /* Not needed anymore */ + + if (!ok) { + 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(num_of_ciphers); + 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; + + tmp_cipher_list = sk_SSL_CIPHER_dup(cipherstack); + if (tmp_cipher_list == NULL) { + goto err; + } + 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; + } + memcpy(pref_list->in_group_flags, in_group_flags, num_in_group_flags); + OPENSSL_free(in_group_flags); + in_group_flags = NULL; + if (*cipher_list != NULL) { + ssl_cipher_preference_list_free(*cipher_list); + } + *cipher_list = pref_list; + pref_list = NULL; + + if (cipher_list_by_id != NULL) { + if (*cipher_list_by_id != NULL) { + sk_SSL_CIPHER_free(*cipher_list_by_id); + } + *cipher_list_by_id = tmp_cipher_list; + tmp_cipher_list = NULL; + (void) sk_SSL_CIPHER_set_cmp_func(*cipher_list_by_id, ssl_cipher_ptr_id_cmp); + + sk_SSL_CIPHER_sort(*cipher_list_by_id); + } else { + sk_SSL_CIPHER_free(tmp_cipher_list); + tmp_cipher_list = NULL; + } + + return cipherstack; + +err: + if (co_list) { + OPENSSL_free(co_list); + } + if (in_group_flags) { + OPENSSL_free(in_group_flags); + } + if (cipherstack) { + sk_SSL_CIPHER_free(cipherstack); + } + if (tmp_cipher_list) { + sk_SSL_CIPHER_free(tmp_cipher_list); + } + if (pref_list && pref_list->in_group_flags) { + OPENSSL_free(pref_list->in_group_flags); + } + if (pref_list) { + OPENSSL_free(pref_list); + } + return NULL; +} + +const char *SSL_CIPHER_description(const SSL_CIPHER *cipher, char *buf, + int len) { + const char *ver; + const char *kx, *au, *enc, *mac; + unsigned long alg_mkey, alg_auth, alg_enc, alg_mac, alg_ssl; + static const char *format = "%-23s %s Kx=%-8s Au=%-4s Enc=%-9s Mac=%-4s\n"; + + alg_mkey = cipher->algorithm_mkey; + alg_auth = cipher->algorithm_auth; + alg_enc = cipher->algorithm_enc; + alg_mac = cipher->algorithm_mac; + alg_ssl = cipher->algorithm_ssl; + + if (alg_ssl & SSL_SSLV3) { + ver = "SSLv3"; + } else if (alg_ssl & SSL_TLSV1_2) { + ver = "TLSv1.2"; + } else { + ver = "unknown"; + } + + switch (alg_mkey) { + case SSL_kRSA: + kx = "RSA"; + break; + + case SSL_kEDH: + kx = "DH"; + break; + + case SSL_kEECDH: + kx = "ECDH"; + break; + + case SSL_kPSK: + kx = "PSK"; + break; + + default: + kx = "unknown"; + } + + switch (alg_auth) { + case SSL_aRSA: + au = "RSA"; + break; + + case SSL_aNULL: + au = "None"; + break; + + case SSL_aECDSA: + au = "ECDSA"; + break; + + case SSL_aPSK: + au = "PSK"; + break; + + default: + au = "unknown"; + break; + } + + switch (alg_enc) { + case SSL_3DES: + enc = "3DES(168)"; + break; + + case SSL_RC4: + enc = "RC4(128)"; + 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; + + default: + enc = "unknown"; + break; + } + + switch (alg_mac) { + case SSL_MD5: + mac = "MD5"; + break; + + 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 "OPENSSL_malloc Error"; + } else if (len < 128) { + return "Buffer too small"; + } + + BIO_snprintf(buf, len, format, cipher->name, ver, kx, au, enc, mac); + return buf; +} + +int SSL_CIPHER_is_AES(const SSL_CIPHER *c) { + return (c->algorithm_enc & SSL_AES) != 0; +} + +int SSL_CIPHER_has_MD5_HMAC(const SSL_CIPHER *c) { + return (c->algorithm_mac & SSL_MD5) != 0; +} + +int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *c) { + return (c->algorithm_mac & (SSL_AES128GCM | SSL_AES256GCM)) != 0; +} + +int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *c) { + return (c->algorithm_enc & SSL_CHACHA20POLY1305) != 0; +} + +const char *SSL_CIPHER_get_version(const SSL_CIPHER *c) { + int i; + + if (c == NULL) { + return "(NONE)"; + } + + i = (int)(c->id >> 24L); + if (i == 3) { + return "TLSv1/SSLv3"; + } else if (i == 2) { + return "SSLv2"; + } else { + return "unknown"; + } +} + +/* return the actual cipher being used */ +const char *SSL_CIPHER_get_name(const SSL_CIPHER *c) { + if (c != NULL) { + return c->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 SSL_TXT_RSA; + + case SSL_kEDH: + switch (cipher->algorithm_auth) { + case SSL_aRSA: + return "DHE_" SSL_TXT_RSA; + case SSL_aNULL: + return SSL_TXT_DH "_anon"; + default: + return "UNKNOWN"; + } + + case SSL_kEECDH: + switch (cipher->algorithm_auth) { + case SSL_aECDSA: + return "ECDHE_" SSL_TXT_ECDSA; + case SSL_aRSA: + return "ECDHE_" SSL_TXT_RSA; + case SSL_aNULL: + return SSL_TXT_ECDH "_anon"; + default: + return "UNKNOWN"; + } + + default: + return "UNKNOWN"; + } +} + +/* number of bits for symmetric cipher */ +int SSL_CIPHER_get_bits(const SSL_CIPHER *c, int *alg_bits) { + int ret = 0; + + if (c != NULL) { + if (alg_bits != NULL) { + *alg_bits = c->alg_bits; + } + ret = c->strength_bits; + } + + return ret; +} + +unsigned long SSL_CIPHER_get_id(const SSL_CIPHER *c) { return c->id; } + +void *SSL_COMP_get_compression_methods(void) { return NULL; } + +int SSL_COMP_add_compression_method(int id, void *cm) { return 1; } + +const char *SSL_COMP_get_name(const void *comp) { return NULL; } + +/* For a cipher return the index corresponding to the certificate type */ +int ssl_cipher_get_cert_index(const SSL_CIPHER *c) { + unsigned long alg_a = c->algorithm_auth; + + if (alg_a & SSL_aECDSA) { + return SSL_PKEY_ECC; + } else if (alg_a & SSL_aRSA) { + return SSL_PKEY_RSA_ENC; + } + + return -1; +} + +/* ssl_cipher_has_server_public_key returns 1 if |cipher| involves a server + * public key in the key exchange, sent in a server Certificate message. + * Otherwise it returns 0. */ +int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher) { + /* Anonymous ciphers do not include a server certificate. */ + if (cipher->algorithm_auth & SSL_aNULL) { + return 0; + } + + /* Neither do PSK ciphers, except for RSA_PSK. */ + if ((cipher->algorithm_auth & SSL_aPSK) && + !(cipher->algorithm_mkey & SSL_kRSA)) { + return 0; + } + + /* All other ciphers include it. */ + return 1; +} + +/* ssl_cipher_requires_server_key_exchange returns 1 if |cipher| requires a + * ServerKeyExchange message. Otherwise it returns 0. + * + * Unlike ssl_cipher_has_server_public_key, some ciphers take optional + * ServerKeyExchanges. PSK and RSA_PSK only use the ServerKeyExchange to + * communicate a psk_identity_hint, so it is optional. */ +int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher) { + /* Ephemeral Diffie-Hellman key exchanges require a ServerKeyExchange. */ + if (cipher->algorithm_mkey & SSL_kEDH || cipher->algorithm_mkey & SSL_kEECDH) { + return 1; + } + + /* It is optional in all others. */ + return 0; +} diff --git a/src/ssl/ssl_error.c b/src/ssl/ssl_error.c new file mode 100644 index 0000000..2ffb9e6 --- /dev/null +++ b/src/ssl/ssl_error.c @@ -0,0 +1,566 @@ +/* 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/err.h> + +#include <openssl/ssl.h> + +const ERR_STRING_DATA SSL_error_string_data[] = { + {ERR_PACK(ERR_LIB_SSL, SSL_F_D2I_SSL_SESSION, 0), "D2I_SSL_SESSION"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_check_private_key, 0), "SSL_CTX_check_private_key"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_new, 0), "SSL_CTX_new"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_set_cipher_list, 0), "SSL_CTX_set_cipher_list"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_set_cipher_list_tls11, 0), "SSL_CTX_set_cipher_list_tls11"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_set_session_id_context, 0), "SSL_CTX_set_session_id_context"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_set_ssl_version, 0), "SSL_CTX_set_ssl_version"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_PrivateKey, 0), "SSL_CTX_use_PrivateKey"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_PrivateKey_ASN1, 0), "SSL_CTX_use_PrivateKey_ASN1"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_PrivateKey_file, 0), "SSL_CTX_use_PrivateKey_file"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_RSAPrivateKey, 0), "SSL_CTX_use_RSAPrivateKey"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_RSAPrivateKey_ASN1, 0), "SSL_CTX_use_RSAPrivateKey_ASN1"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_RSAPrivateKey_file, 0), "SSL_CTX_use_RSAPrivateKey_file"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_authz, 0), "SSL_CTX_use_authz"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_certificate, 0), "SSL_CTX_use_certificate"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_certificate_ASN1, 0), "SSL_CTX_use_certificate_ASN1"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_certificate_chain_file, 0), "SSL_CTX_use_certificate_chain_file"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_certificate_file, 0), "SSL_CTX_use_certificate_file"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_CTX_use_psk_identity_hint, 0), "SSL_CTX_use_psk_identity_hint"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_new, 0), "SSL_SESSION_new"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_print_fp, 0), "SSL_SESSION_print_fp"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_set1_id_context, 0), "SSL_SESSION_set1_id_context"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_to_bytes_full, 0), "SSL_SESSION_to_bytes_full"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_accept, 0), "SSL_accept"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_add_dir_cert_subjects_to_stack, 0), "SSL_add_dir_cert_subjects_to_stack"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_add_file_cert_subjects_to_stack, 0), "SSL_add_file_cert_subjects_to_stack"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_check_private_key, 0), "SSL_check_private_key"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_clear, 0), "SSL_clear"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_connect, 0), "SSL_connect"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_do_handshake, 0), "SSL_do_handshake"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_load_client_CA_file, 0), "SSL_load_client_CA_file"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_new, 0), "SSL_new"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_peek, 0), "SSL_peek"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_read, 0), "SSL_read"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_cipher_list, 0), "SSL_set_cipher_list"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_fd, 0), "SSL_set_fd"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_rfd, 0), "SSL_set_rfd"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_session, 0), "SSL_set_session"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_session_id_context, 0), "SSL_set_session_id_context"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_session_ticket_ext, 0), "SSL_set_session_ticket_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_set_wfd, 0), "SSL_set_wfd"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_shutdown, 0), "SSL_shutdown"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_PrivateKey, 0), "SSL_use_PrivateKey"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_PrivateKey_ASN1, 0), "SSL_use_PrivateKey_ASN1"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_PrivateKey_file, 0), "SSL_use_PrivateKey_file"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_RSAPrivateKey, 0), "SSL_use_RSAPrivateKey"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_RSAPrivateKey_ASN1, 0), "SSL_use_RSAPrivateKey_ASN1"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_RSAPrivateKey_file, 0), "SSL_use_RSAPrivateKey_file"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_authz, 0), "SSL_use_authz"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_certificate, 0), "SSL_use_certificate"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_certificate_ASN1, 0), "SSL_use_certificate_ASN1"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_certificate_file, 0), "SSL_use_certificate_file"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_use_psk_identity_hint, 0), "SSL_use_psk_identity_hint"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_write, 0), "SSL_write"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_authz_find_data, 0), "authz_find_data"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_check_suiteb_cipher_list, 0), "check_suiteb_cipher_list"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION, 0), "d2i_SSL_SESSION"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION_get_octet_string, 0), "d2i_SSL_SESSION_get_octet_string"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION_get_string, 0), "d2i_SSL_SESSION_get_string"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_do_dtls1_write, 0), "do_dtls1_write"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_do_ssl3_write, 0), "do_ssl3_write"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_accept, 0), "dtls1_accept"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_buffer_record, 0), "dtls1_buffer_record"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_check_timeout_num, 0), "dtls1_check_timeout_num"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_connect, 0), "dtls1_connect"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_do_write, 0), "dtls1_do_write"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_get_hello_verify, 0), "dtls1_get_hello_verify"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_get_message, 0), "dtls1_get_message"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_get_message_fragment, 0), "dtls1_get_message_fragment"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_heartbeat, 0), "dtls1_heartbeat"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_preprocess_fragment, 0), "dtls1_preprocess_fragment"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_process_record, 0), "dtls1_process_record"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_read_bytes, 0), "dtls1_read_bytes"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_send_hello_verify_request, 0), "dtls1_send_hello_verify_request"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_write_app_data_bytes, 0), "dtls1_write_app_data_bytes"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_fclose, 0), "fclose"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_fprintf, 0), "fprintf"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_i2d_SSL_SESSION, 0), "i2d_SSL_SESSION"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_printf, 0), "printf"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_read_authz, 0), "read_authz"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_accept, 0), "ssl23_accept"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_client_hello, 0), "ssl23_client_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_connect, 0), "ssl23_connect"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_client_hello, 0), "ssl23_get_client_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_server_hello, 0), "ssl23_get_server_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_get_v2_client_hello, 0), "ssl23_get_v2_client_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_peek, 0), "ssl23_peek"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_read, 0), "ssl23_read"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_write, 0), "ssl23_write"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_accept, 0), "ssl3_accept"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_callback_ctrl, 0), "ssl3_callback_ctrl"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_cert_verify_hash, 0), "ssl3_cert_verify_hash"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_change_cipher_state, 0), "ssl3_change_cipher_state"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_check_cert_and_algorithm, 0), "ssl3_check_cert_and_algorithm"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_check_client_hello, 0), "ssl3_check_client_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_connect, 0), "ssl3_connect"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_ctrl, 0), "ssl3_ctrl"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_ctx_ctrl, 0), "ssl3_ctx_ctrl"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_digest_cached_records, 0), "ssl3_digest_cached_records"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_do_change_cipher_spec, 0), "ssl3_do_change_cipher_spec"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_expect_change_cipher_spec, 0), "ssl3_expect_change_cipher_spec"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_generate_key_block, 0), "ssl3_generate_key_block"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_cert_status, 0), "ssl3_get_cert_status"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_cert_verify, 0), "ssl3_get_cert_verify"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_certificate_request, 0), "ssl3_get_certificate_request"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_channel_id, 0), "ssl3_get_channel_id"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_client_certificate, 0), "ssl3_get_client_certificate"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_client_hello, 0), "ssl3_get_client_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_client_key_exchange, 0), "ssl3_get_client_key_exchange"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_finished, 0), "ssl3_get_finished"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_initial_bytes, 0), "ssl3_get_initial_bytes"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_message, 0), "ssl3_get_message"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_new_session_ticket, 0), "ssl3_get_new_session_ticket"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_next_proto, 0), "ssl3_get_next_proto"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_record, 0), "ssl3_get_record"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_certificate, 0), "ssl3_get_server_certificate"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_done, 0), "ssl3_get_server_done"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_hello, 0), "ssl3_get_server_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_server_key_exchange, 0), "ssl3_get_server_key_exchange"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_get_v2_client_hello, 0), "ssl3_get_v2_client_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_handshake_mac, 0), "ssl3_handshake_mac"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_prf, 0), "ssl3_prf"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_read_bytes, 0), "ssl3_read_bytes"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_read_n, 0), "ssl3_read_n"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_cert_verify, 0), "ssl3_send_cert_verify"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_certificate_request, 0), "ssl3_send_certificate_request"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_channel_id, 0), "ssl3_send_channel_id"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_certificate, 0), "ssl3_send_client_certificate"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_hello, 0), "ssl3_send_client_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_key_exchange, 0), "ssl3_send_client_key_exchange"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_new_session_ticket, 0), "ssl3_send_new_session_ticket"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_certificate, 0), "ssl3_send_server_certificate"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_hello, 0), "ssl3_send_server_hello"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_key_exchange, 0), "ssl3_send_server_key_exchange"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_setup_key_block, 0), "ssl3_setup_key_block"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_setup_read_buffer, 0), "ssl3_setup_read_buffer"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_setup_write_buffer, 0), "ssl3_setup_write_buffer"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_write_bytes, 0), "ssl3_write_bytes"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_write_pending, 0), "ssl3_write_pending"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_cert_chain, 0), "ssl_add_cert_chain"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_cert_to_buf, 0), "ssl_add_cert_to_buf"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_clienthello_renegotiate_ext, 0), "ssl_add_clienthello_renegotiate_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_clienthello_tlsext, 0), "ssl_add_clienthello_tlsext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_clienthello_use_srtp_ext, 0), "ssl_add_clienthello_use_srtp_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_serverhello_renegotiate_ext, 0), "ssl_add_serverhello_renegotiate_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_serverhello_tlsext, 0), "ssl_add_serverhello_tlsext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_add_serverhello_use_srtp_ext, 0), "ssl_add_serverhello_use_srtp_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_bad_method, 0), "ssl_bad_method"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_build_cert_chain, 0), "ssl_build_cert_chain"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_bytes_to_cipher_list, 0), "ssl_bytes_to_cipher_list"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cert_dup, 0), "ssl_cert_dup"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cert_inst, 0), "ssl_cert_inst"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cert_new, 0), "ssl_cert_new"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_check_serverhello_tlsext, 0), "ssl_check_serverhello_tlsext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_check_srvr_ecc_cert_and_alg, 0), "ssl_check_srvr_ecc_cert_and_alg"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cipher_process_rulestr, 0), "ssl_cipher_process_rulestr"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_cipher_strength_sort, 0), "ssl_cipher_strength_sort"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_create_cipher_list, 0), "ssl_create_cipher_list"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_log_master_secret, 0), "ssl_ctx_log_master_secret"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_log_rsa_client_key_exchange, 0), "ssl_ctx_log_rsa_client_key_exchange"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_ctx_make_profiles, 0), "ssl_ctx_make_profiles"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_new_session, 0), "ssl_get_new_session"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_prev_session, 0), "ssl_get_prev_session"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_server_cert_index, 0), "ssl_get_server_cert_index"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_get_sign_pkey, 0), "ssl_get_sign_pkey"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_init_wbio_buffer, 0), "ssl_init_wbio_buffer"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_new, 0), "ssl_new"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_clienthello_renegotiate_ext, 0), "ssl_parse_clienthello_renegotiate_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_clienthello_tlsext, 0), "ssl_parse_clienthello_tlsext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_clienthello_use_srtp_ext, 0), "ssl_parse_clienthello_use_srtp_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_serverhello_renegotiate_ext, 0), "ssl_parse_serverhello_renegotiate_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_serverhello_tlsext, 0), "ssl_parse_serverhello_tlsext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_parse_serverhello_use_srtp_ext, 0), "ssl_parse_serverhello_use_srtp_ext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_prepare_clienthello_tlsext, 0), "ssl_prepare_clienthello_tlsext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_scan_clienthello_tlsext, 0), "ssl_scan_clienthello_tlsext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_scan_serverhello_tlsext, 0), "ssl_scan_serverhello_tlsext"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_sess_cert_new, 0), "ssl_sess_cert_new"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_set_authz, 0), "ssl_set_authz"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_set_cert, 0), "ssl_set_cert"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_set_pkey, 0), "ssl_set_pkey"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_undefined_const_function, 0), "ssl_undefined_const_function"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_undefined_function, 0), "ssl_undefined_function"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_undefined_void_function, 0), "ssl_undefined_void_function"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl_verify_cert_chain, 0), "ssl_verify_cert_chain"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls12_check_peer_sigalg, 0), "tls12_check_peer_sigalg"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_aead_ctx_init, 0), "tls1_aead_ctx_init"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_cert_verify_mac, 0), "tls1_cert_verify_mac"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_change_cipher_state, 0), "tls1_change_cipher_state"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_change_cipher_state_aead, 0), "tls1_change_cipher_state_aead"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_change_cipher_state_cipher, 0), "tls1_change_cipher_state_cipher"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_check_duplicate_extensions, 0), "tls1_check_duplicate_extensions"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_enc, 0), "tls1_enc"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_export_keying_material, 0), "tls1_export_keying_material"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_get_server_supplemental_data, 0), "tls1_get_server_supplemental_data"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_heartbeat, 0), "tls1_heartbeat"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_prf, 0), "tls1_prf"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_send_server_supplemental_data, 0), "tls1_send_server_supplemental_data"}, + {ERR_PACK(ERR_LIB_SSL, SSL_F_tls1_setup_key_block, 0), "tls1_setup_key_block"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_APP_DATA_IN_HANDSHAKE), "APP_DATA_IN_HANDSHAKE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT), "ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_AUTHZ_DATA_TOO_LARGE), "AUTHZ_DATA_TOO_LARGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ALERT), "BAD_ALERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ALERT_RECORD), "BAD_ALERT_RECORD"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_AUTHENTICATION_TYPE), "BAD_AUTHENTICATION_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CHANGE_CIPHER_SPEC), "BAD_CHANGE_CIPHER_SPEC"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_CHECKSUM), "BAD_CHECKSUM"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA), "BAD_DATA"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK), "BAD_DATA_RETURNED_BY_CALLBACK"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DECOMPRESSION), "BAD_DECOMPRESSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DH_G_LENGTH), "BAD_DH_G_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DH_PUB_KEY_LENGTH), "BAD_DH_PUB_KEY_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DH_P_LENGTH), "BAD_DH_P_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DIGEST_LENGTH), "BAD_DIGEST_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_DSA_SIGNATURE), "BAD_DSA_SIGNATURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECC_CERT), "BAD_ECC_CERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECDSA_SIGNATURE), "BAD_ECDSA_SIGNATURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_ECPOINT), "BAD_ECPOINT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HANDSHAKE_LENGTH), "BAD_HANDSHAKE_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_HELLO_REQUEST), "BAD_HELLO_REQUEST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_LENGTH), "BAD_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_MAC_DECODE), "BAD_MAC_DECODE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_MAC_LENGTH), "BAD_MAC_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_MESSAGE_TYPE), "BAD_MESSAGE_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PACKET_LENGTH), "BAD_PACKET_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PROTOCOL_VERSION_NUMBER), "BAD_PROTOCOL_VERSION_NUMBER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_PSK_IDENTITY_HINT_LENGTH), "BAD_PSK_IDENTITY_HINT_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RESPONSE_ARGUMENT), "BAD_RESPONSE_ARGUMENT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_DECRYPT), "BAD_RSA_DECRYPT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_ENCRYPT), "BAD_RSA_ENCRYPT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_E_LENGTH), "BAD_RSA_E_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_MODULUS_LENGTH), "BAD_RSA_MODULUS_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_RSA_SIGNATURE), "BAD_RSA_SIGNATURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SIGNATURE), "BAD_SIGNATURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_A_LENGTH), "BAD_SRP_A_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_B_LENGTH), "BAD_SRP_B_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_G_LENGTH), "BAD_SRP_G_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_N_LENGTH), "BAD_SRP_N_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRP_S_LENGTH), "BAD_SRP_S_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRTP_MKI_VALUE), "BAD_SRTP_MKI_VALUE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST), "BAD_SRTP_PROTECTION_PROFILE_LIST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SSL_FILETYPE), "BAD_SSL_FILETYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_SSL_SESSION_ID_LENGTH), "BAD_SSL_SESSION_ID_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_STATE), "BAD_STATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_VALUE), "BAD_VALUE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BAD_WRITE_RETRY), "BAD_WRITE_RETRY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BIO_NOT_SET), "BIO_NOT_SET"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BLOCK_CIPHER_PAD_IS_WRONG), "BLOCK_CIPHER_PAD_IS_WRONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_BN_LIB), "BN_LIB"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CANNOT_SERIALIZE_PUBLIC_KEY), "CANNOT_SERIALIZE_PUBLIC_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CA_DN_LENGTH_MISMATCH), "CA_DN_LENGTH_MISMATCH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CA_DN_TOO_LONG), "CA_DN_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CCS_RECEIVED_EARLY), "CCS_RECEIVED_EARLY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED), "CERTIFICATE_VERIFY_FAILED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERT_CB_ERROR), "CERT_CB_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERT_LENGTH_MISMATCH), "CERT_LENGTH_MISMATCH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CHALLENGE_IS_DIFFERENT), "CHALLENGE_IS_DIFFERENT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CHANNEL_ID_NOT_P256), "CHANNEL_ID_NOT_P256"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CHANNEL_ID_SIGNATURE_INVALID), "CHANNEL_ID_SIGNATURE_INVALID"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_CODE_WRONG_LENGTH), "CIPHER_CODE_WRONG_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_OR_HASH_UNAVAILABLE), "CIPHER_OR_HASH_UNAVAILABLE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CIPHER_TABLE_SRC_ERROR), "CIPHER_TABLE_SRC_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CLIENTHELLO_PARSE_FAILED), "CLIENTHELLO_PARSE_FAILED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CLIENTHELLO_TLSEXT), "CLIENTHELLO_TLSEXT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSED_LENGTH_TOO_LONG), "COMPRESSED_LENGTH_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_DISABLED), "COMPRESSION_DISABLED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_FAILURE), "COMPRESSION_FAILURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE), "COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COMPRESSION_LIBRARY_ERROR), "COMPRESSION_LIBRARY_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONNECTION_ID_IS_DIFFERENT), "CONNECTION_ID_IS_DIFFERENT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONNECTION_REJECTED), "CONNECTION_REJECTED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CONNECTION_TYPE_NOT_SET), "CONNECTION_TYPE_NOT_SET"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_COOKIE_MISMATCH), "COOKIE_MISMATCH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_D2I_ECDSA_SIG), "D2I_ECDSA_SIG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED), "DATA_BETWEEN_CCS_AND_FINISHED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DATA_LENGTH_TOO_LONG), "DATA_LENGTH_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DECODE_ERROR), "DECODE_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DECRYPTION_FAILED), "DECRYPTION_FAILED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC), "DECRYPTION_FAILED_OR_BAD_RECORD_MAC"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG), "DH_PUBLIC_VALUE_LENGTH_IS_WRONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DIGEST_CHECK_FAILED), "DIGEST_CHECK_FAILED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DTLS_MESSAGE_TOO_BIG), "DTLS_MESSAGE_TOO_BIG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_DUPLICATE_COMPRESSION_ID), "DUPLICATE_COMPRESSION_ID"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_NOT_FOR_KEY_AGREEMENT), "ECC_CERT_NOT_FOR_KEY_AGREEMENT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_NOT_FOR_SIGNING), "ECC_CERT_NOT_FOR_SIGNING"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_SHOULD_HAVE_RSA_SIGNATURE), "ECC_CERT_SHOULD_HAVE_RSA_SIGNATURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECC_CERT_SHOULD_HAVE_SHA1_SIGNATURE), "ECC_CERT_SHOULD_HAVE_SHA1_SIGNATURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER), "ECGROUP_TOO_LARGE_FOR_CIPHER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST), "EMPTY_SRTP_PROTECTION_PROFILE_LIST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ENCRYPTED_LENGTH_TOO_LONG), "ENCRYPTED_LENGTH_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ERROR_GENERATING_TMP_RSA_KEY), "ERROR_GENERATING_TMP_RSA_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST), "ERROR_IN_RECEIVED_CIPHER_LIST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EVP_DIGESTSIGNFINAL_FAILED), "EVP_DIGESTSIGNFINAL_FAILED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EVP_DIGESTSIGNINIT_FAILED), "EVP_DIGESTSIGNINIT_FAILED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXCESSIVE_MESSAGE_SIZE), "EXCESSIVE_MESSAGE_SIZE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_EXTRA_DATA_IN_MESSAGE), "EXTRA_DATA_IN_MESSAGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOST_NOT_SUPPORTED), "GOST_NOT_SUPPORTED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_A_FIN_BEFORE_A_CCS), "GOT_A_FIN_BEFORE_A_CCS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_CHANNEL_ID_BEFORE_A_CCS), "GOT_CHANNEL_ID_BEFORE_A_CCS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS), "GOT_NEXT_PROTO_BEFORE_A_CCS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION), "GOT_NEXT_PROTO_WITHOUT_EXTENSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HANDSHAKE_FAILURE_ON_CLIENT_HELLO), "HANDSHAKE_FAILURE_ON_CLIENT_HELLO"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HANDSHAKE_RECORD_BEFORE_CCS), "HANDSHAKE_RECORD_BEFORE_CCS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HTTPS_PROXY_REQUEST), "HTTPS_PROXY_REQUEST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_HTTP_REQUEST), "HTTP_REQUEST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ILLEGAL_PADDING), "ILLEGAL_PADDING"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ILLEGAL_SUITEB_DIGEST), "ILLEGAL_SUITEB_DIGEST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INAPPROPRIATE_FALLBACK), "INAPPROPRIATE_FALLBACK"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INCONSISTENT_COMPRESSION), "INCONSISTENT_COMPRESSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_AUDIT_PROOF), "INVALID_AUDIT_PROOF"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_AUTHZ_DATA), "INVALID_AUTHZ_DATA"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_CHALLENGE_LENGTH), "INVALID_CHALLENGE_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMMAND), "INVALID_COMMAND"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_COMPRESSION_ALGORITHM), "INVALID_COMPRESSION_ALGORITHM"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_MESSAGE), "INVALID_MESSAGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_NULL_CMD_NAME), "INVALID_NULL_CMD_NAME"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_PURPOSE), "INVALID_PURPOSE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SERVERINFO_DATA), "INVALID_SERVERINFO_DATA"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SRP_USERNAME), "INVALID_SRP_USERNAME"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SSL_SESSION), "INVALID_SSL_SESSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_STATUS_RESPONSE), "INVALID_STATUS_RESPONSE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_TICKET_KEYS_LENGTH), "INVALID_TICKET_KEYS_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_TRUST), "INVALID_TRUST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KEY_ARG_TOO_LONG), "KEY_ARG_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5), "KRB5"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_C_CC_PRINC), "KRB5_C_CC_PRINC"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_C_GET_CRED), "KRB5_C_GET_CRED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_C_INIT), "KRB5_C_INIT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_C_MK_REQ), "KRB5_C_MK_REQ"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_BAD_TICKET), "KRB5_S_BAD_TICKET"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_INIT), "KRB5_S_INIT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_RD_REQ), "KRB5_S_RD_REQ"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_TKT_EXPIRED), "KRB5_S_TKT_EXPIRED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_TKT_NYV), "KRB5_S_TKT_NYV"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_KRB5_S_TKT_SKEW), "KRB5_S_TKT_SKEW"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LENGTH_MISMATCH), "LENGTH_MISMATCH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LENGTH_TOO_SHORT), "LENGTH_TOO_SHORT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LIBRARY_BUG), "LIBRARY_BUG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_LIBRARY_HAS_NO_CIPHERS), "LIBRARY_HAS_NO_CIPHERS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MESSAGE_TOO_LONG), "MESSAGE_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DH_DSA_CERT), "MISSING_DH_DSA_CERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DH_KEY), "MISSING_DH_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DH_RSA_CERT), "MISSING_DH_RSA_CERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_DSA_SIGNING_CERT), "MISSING_DSA_SIGNING_CERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_ECDH_CERT), "MISSING_ECDH_CERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_ECDSA_SIGNING_CERT), "MISSING_ECDSA_SIGNING_CERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_EXPORT_TMP_DH_KEY), "MISSING_EXPORT_TMP_DH_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_EXPORT_TMP_RSA_KEY), "MISSING_EXPORT_TMP_RSA_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_CERTIFICATE), "MISSING_RSA_CERTIFICATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_ENCRYPTING_CERT), "MISSING_RSA_ENCRYPTING_CERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_RSA_SIGNING_CERT), "MISSING_RSA_SIGNING_CERT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_SRP_PARAM), "MISSING_SRP_PARAM"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_DH_KEY), "MISSING_TMP_DH_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_ECDH_KEY), "MISSING_TMP_ECDH_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_RSA_KEY), "MISSING_TMP_RSA_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_TMP_RSA_PKEY), "MISSING_TMP_RSA_PKEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MISSING_VERIFY_MESSAGE), "MISSING_VERIFY_MESSAGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MIXED_SPECIAL_OPERATOR_WITH_GROUPS), "MIXED_SPECIAL_OPERATOR_WITH_GROUPS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MTU_TOO_SMALL), "MTU_TOO_SMALL"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_MULTIPLE_SGC_RESTARTS), "MULTIPLE_SGC_RESTARTS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NESTED_GROUP), "NESTED_GROUP"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NON_SSLV2_INITIAL_PACKET), "NON_SSLV2_INITIAL_PACKET"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATES_RETURNED), "NO_CERTIFICATES_RETURNED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_ASSIGNED), "NO_CERTIFICATE_ASSIGNED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_RETURNED), "NO_CERTIFICATE_RETURNED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_SET), "NO_CERTIFICATE_SET"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CERTIFICATE_SPECIFIED), "NO_CERTIFICATE_SPECIFIED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHERS_AVAILABLE), "NO_CIPHERS_AVAILABLE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHERS_PASSED), "NO_CIPHERS_PASSED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHERS_SPECIFIED), "NO_CIPHERS_SPECIFIED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHER_LIST), "NO_CIPHER_LIST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CIPHER_MATCH), "NO_CIPHER_MATCH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CLIENT_CERT_METHOD), "NO_CLIENT_CERT_METHOD"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_CLIENT_CERT_RECEIVED), "NO_CLIENT_CERT_RECEIVED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_COMPRESSION_SPECIFIED), "NO_COMPRESSION_SPECIFIED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER), "NO_GOST_CERTIFICATE_SENT_BY_PEER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_METHOD_SPECIFIED), "NO_METHOD_SPECIFIED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_P256_SUPPORT), "NO_P256_SUPPORT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PEM_EXTENSIONS), "NO_PEM_EXTENSIONS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PRIVATEKEY), "NO_PRIVATEKEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PRIVATE_KEY_ASSIGNED), "NO_PRIVATE_KEY_ASSIGNED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PROTOCOLS_AVAILABLE), "NO_PROTOCOLS_AVAILABLE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_PUBLICKEY), "NO_PUBLICKEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_RENEGOTIATION), "NO_RENEGOTIATION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_REQUIRED_DIGEST), "NO_REQUIRED_DIGEST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SHARED_CIPHER), "NO_SHARED_CIPHER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SHARED_SIGATURE_ALGORITHMS), "NO_SHARED_SIGATURE_ALGORITHMS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_SRTP_PROFILES), "NO_SRTP_PROFILES"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NO_VERIFY_CALLBACK), "NO_VERIFY_CALLBACK"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NULL_SSL_CTX), "NULL_SSL_CTX"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_NULL_SSL_METHOD_PASSED), "NULL_SSL_METHOD_PASSED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED), "OLD_SESSION_CIPHER_NOT_RETURNED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED), "OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE), "ONLY_DTLS_1_2_ALLOWED_IN_SUITEB_MODE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE), "ONLY_TLS_1_2_ALLOWED_IN_SUITEB_MODE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE), "ONLY_TLS_ALLOWED_IN_FIPS_MODE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_OPAQUE_PRF_INPUT_TOO_LONG), "OPAQUE_PRF_INPUT_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PACKET_LENGTH_TOO_LONG), "PACKET_LENGTH_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PARSE_TLSEXT), "PARSE_TLSEXT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PATH_TOO_LONG), "PATH_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE), "PEER_DID_NOT_RETURN_A_CERTIFICATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR), "PEER_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR_CERTIFICATE), "PEER_ERROR_CERTIFICATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR_NO_CERTIFICATE), "PEER_ERROR_NO_CERTIFICATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR_NO_CIPHER), "PEER_ERROR_NO_CIPHER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE), "PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEM_NAME_BAD_PREFIX), "PEM_NAME_BAD_PREFIX"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PEM_NAME_TOO_SHORT), "PEM_NAME_TOO_SHORT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PRE_MAC_LENGTH_TOO_LONG), "PRE_MAC_LENGTH_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS), "PROBLEMS_MAPPING_CIPHER_FUNCTIONS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PROTOCOL_IS_SHUTDOWN), "PROTOCOL_IS_SHUTDOWN"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_IDENTITY_NOT_FOUND), "PSK_IDENTITY_NOT_FOUND"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_NO_CLIENT_CB), "PSK_NO_CLIENT_CB"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PSK_NO_SERVER_CB), "PSK_NO_SERVER_CB"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PUBLIC_KEY_ENCRYPT_ERROR), "PUBLIC_KEY_ENCRYPT_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PUBLIC_KEY_IS_NOT_RSA), "PUBLIC_KEY_IS_NOT_RSA"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_PUBLIC_KEY_NOT_RSA), "PUBLIC_KEY_NOT_RSA"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_READ_BIO_NOT_SET), "READ_BIO_NOT_SET"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_READ_TIMEOUT_EXPIRED), "READ_TIMEOUT_EXPIRED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_READ_WRONG_PACKET_TYPE), "READ_WRONG_PACKET_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_LENGTH_MISMATCH), "RECORD_LENGTH_MISMATCH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_TOO_LARGE), "RECORD_TOO_LARGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RECORD_TOO_SMALL), "RECORD_TOO_SMALL"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATE_EXT_TOO_LONG), "RENEGOTIATE_EXT_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATION_ENCODING_ERR), "RENEGOTIATION_ENCODING_ERR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_RENEGOTIATION_MISMATCH), "RENEGOTIATION_MISMATCH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_CIPHER_MISSING), "REQUIRED_CIPHER_MISSING"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REQUIRED_COMPRESSSION_ALGORITHM_MISSING), "REQUIRED_COMPRESSSION_ALGORITHM_MISSING"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REUSE_CERT_LENGTH_NOT_ZERO), "REUSE_CERT_LENGTH_NOT_ZERO"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REUSE_CERT_TYPE_NOT_ZERO), "REUSE_CERT_TYPE_NOT_ZERO"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_REUSE_CIPHER_LIST_NOT_ZERO), "REUSE_CIPHER_LIST_NOT_ZERO"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING), "SCSV_RECEIVED_WHEN_RENEGOTIATING"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SERVERHELLO_TLSEXT), "SERVERHELLO_TLSEXT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED), "SESSION_ID_CONTEXT_UNINITIALIZED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SESSION_MAY_NOT_BE_CREATED), "SESSION_MAY_NOT_BE_CREATED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ), "SHORT_READ"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SIGNATURE_ALGORITHMS_ERROR), "SIGNATURE_ALGORITHMS_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE), "SIGNATURE_FOR_NON_SIGNING_CERTIFICATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRP_A_CALC), "SRP_A_CALC"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_COULD_NOT_ALLOCATE_PROFILES), "SRTP_COULD_NOT_ALLOCATE_PROFILES"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG), "SRTP_PROTECTION_PROFILE_LIST_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SRTP_UNKNOWN_PROTECTION_PROFILE), "SRTP_UNKNOWN_PROTECTION_PROFILE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL23_DOING_SESSION_ID_REUSE), "SSL23_DOING_SESSION_ID_REUSE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL2_CONNECTION_ID_TOO_LONG), "SSL2_CONNECTION_ID_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_ECPOINTFORMAT), "SSL3_EXT_INVALID_ECPOINTFORMAT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_SERVERNAME), "SSL3_EXT_INVALID_SERVERNAME"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE), "SSL3_EXT_INVALID_SERVERNAME_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_SESSION_ID_TOO_LONG), "SSL3_SESSION_ID_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL3_SESSION_ID_TOO_SHORT), "SSL3_SESSION_ID_TOO_SHORT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE), "SSLV3_ALERT_BAD_CERTIFICATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_BAD_RECORD_MAC), "SSLV3_ALERT_BAD_RECORD_MAC"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED), "SSLV3_ALERT_CERTIFICATE_EXPIRED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED), "SSLV3_ALERT_CERTIFICATE_REVOKED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN), "SSLV3_ALERT_CERTIFICATE_UNKNOWN"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_CLOSE_NOTIFY), "SSLV3_ALERT_CLOSE_NOTIFY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE), "SSLV3_ALERT_DECOMPRESSION_FAILURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE), "SSLV3_ALERT_HANDSHAKE_FAILURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER), "SSLV3_ALERT_ILLEGAL_PARAMETER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_NO_CERTIFICATE), "SSLV3_ALERT_NO_CERTIFICATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE), "SSLV3_ALERT_UNEXPECTED_MESSAGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE), "SSLV3_ALERT_UNSUPPORTED_CERTIFICATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION), "SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_HANDSHAKE_FAILURE), "SSL_HANDSHAKE_FAILURE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS), "SSL_LIBRARY_HAS_NO_CIPHERS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED), "SSL_SESSION_ID_CALLBACK_FAILED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CONFLICT), "SSL_SESSION_ID_CONFLICT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG), "SSL_SESSION_ID_CONTEXT_TOO_LONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH), "SSL_SESSION_ID_HAS_BAD_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SSL_SESSION_ID_IS_DIFFERENT), "SSL_SESSION_ID_IS_DIFFERENT"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_ACCESS_DENIED), "TLSV1_ALERT_ACCESS_DENIED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECODE_ERROR), "TLSV1_ALERT_DECODE_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED), "TLSV1_ALERT_DECRYPTION_FAILED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_DECRYPT_ERROR), "TLSV1_ALERT_DECRYPT_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION), "TLSV1_ALERT_EXPORT_RESTRICTION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INAPPROPRIATE_FALLBACK), "TLSV1_ALERT_INAPPROPRIATE_FALLBACK"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY), "TLSV1_ALERT_INSUFFICIENT_SECURITY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_INTERNAL_ERROR), "TLSV1_ALERT_INTERNAL_ERROR"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION), "TLSV1_ALERT_NO_RENEGOTIATION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION), "TLSV1_ALERT_PROTOCOL_VERSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW), "TLSV1_ALERT_RECORD_OVERFLOW"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_UNKNOWN_CA), "TLSV1_ALERT_UNKNOWN_CA"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_ALERT_USER_CANCELLED), "TLSV1_ALERT_USER_CANCELLED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE), "TLSV1_BAD_CERTIFICATE_HASH_VALUE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE), "TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE), "TLSV1_CERTIFICATE_UNOBTAINABLE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_UNRECOGNIZED_NAME), "TLSV1_UNRECOGNIZED_NAME"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLSV1_UNSUPPORTED_EXTENSION), "TLSV1_UNSUPPORTED_EXTENSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER), "TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_ILLEGAL_EXPORTER_LABEL), "TLS_ILLEGAL_EXPORTER_LABEL"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST), "TLS_INVALID_ECPOINTFORMAT_LIST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST), "TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG), "TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TOO_MANY_EMPTY_FRAGMENTS), "TOO_MANY_EMPTY_FRAGMENTS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_TRIED_TO_USE_UNSUPPORTED_CIPHER), "TRIED_TO_USE_UNSUPPORTED_CIPHER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_DECODE_DH_CERTS), "UNABLE_TO_DECODE_DH_CERTS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_DECODE_ECDH_CERTS), "UNABLE_TO_DECODE_ECDH_CERTS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_EXTRACT_PUBLIC_KEY), "UNABLE_TO_EXTRACT_PUBLIC_KEY"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_DH_PARAMETERS), "UNABLE_TO_FIND_DH_PARAMETERS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS), "UNABLE_TO_FIND_ECDH_PARAMETERS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS), "UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_FIND_SSL_METHOD), "UNABLE_TO_FIND_SSL_METHOD"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES), "UNABLE_TO_LOAD_SSL2_MD5_ROUTINES"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES), "UNABLE_TO_LOAD_SSL3_MD5_ROUTINES"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES), "UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_GROUP_CLOSE), "UNEXPECTED_GROUP_CLOSE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_MESSAGE), "UNEXPECTED_MESSAGE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_OPERATOR_IN_GROUP), "UNEXPECTED_OPERATOR_IN_GROUP"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNEXPECTED_RECORD), "UNEXPECTED_RECORD"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNINITIALIZED), "UNINITIALIZED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_ALERT_TYPE), "UNKNOWN_ALERT_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_AUTHZ_DATA_TYPE), "UNKNOWN_AUTHZ_DATA_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CERTIFICATE_TYPE), "UNKNOWN_CERTIFICATE_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CIPHER_RETURNED), "UNKNOWN_CIPHER_RETURNED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CIPHER_TYPE), "UNKNOWN_CIPHER_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_CMD_NAME), "UNKNOWN_CMD_NAME"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_DIGEST), "UNKNOWN_DIGEST"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE), "UNKNOWN_KEY_EXCHANGE_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_PKEY_TYPE), "UNKNOWN_PKEY_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_PROTOCOL), "UNKNOWN_PROTOCOL"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_REMOTE_ERROR_TYPE), "UNKNOWN_REMOTE_ERROR_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_SSL_VERSION), "UNKNOWN_SSL_VERSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_STATE), "UNKNOWN_STATE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNKNOWN_SUPPLEMENTAL_DATA_TYPE), "UNKNOWN_SUPPLEMENTAL_DATA_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNPROCESSED_HANDSHAKE_DATA), "UNPROCESSED_HANDSHAKE_DATA"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED), "UNSAFE_LEGACY_RENEGOTIATION_DISABLED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_CIPHER), "UNSUPPORTED_CIPHER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM), "UNSUPPORTED_COMPRESSION_ALGORITHM"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_DIGEST_TYPE), "UNSUPPORTED_DIGEST_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE), "UNSUPPORTED_ELLIPTIC_CURVE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_PROTOCOL), "UNSUPPORTED_PROTOCOL"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_SSL_VERSION), "UNSUPPORTED_SSL_VERSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_UNSUPPORTED_STATUS_TYPE), "UNSUPPORTED_STATUS_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_USE_SRTP_NOT_NEGOTIATED), "USE_SRTP_NOT_NEGOTIATED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRITE_BIO_NOT_SET), "WRITE_BIO_NOT_SET"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CERTIFICATE_TYPE), "WRONG_CERTIFICATE_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CIPHER_RETURNED), "WRONG_CIPHER_RETURNED"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_CURVE), "WRONG_CURVE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_MESSAGE_TYPE), "WRONG_MESSAGE_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_NUMBER_OF_KEY_BITS), "WRONG_NUMBER_OF_KEY_BITS"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_LENGTH), "WRONG_SIGNATURE_LENGTH"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_SIZE), "WRONG_SIGNATURE_SIZE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SIGNATURE_TYPE), "WRONG_SIGNATURE_TYPE"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_SSL_VERSION), "WRONG_SSL_VERSION"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_WRONG_VERSION_NUMBER), "WRONG_VERSION_NUMBER"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_X509_LIB), "X509_LIB"}, + {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS), "X509_VERIFICATION_SETUP_PROBLEMS"}, + {0, NULL}, +}; diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c new file mode 100644 index 0000000..c3b95d5 --- /dev/null +++ b/src/ssl/ssl_lib.c @@ -0,0 +1,3174 @@ +/* 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 <stdio.h> +#include <assert.h> + +#include <openssl/bytestring.h> +#include <openssl/dh.h> +#include <openssl/engine.h> +#include <openssl/lhash.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/rand.h> +#include <openssl/x509v3.h> + +#include "ssl_locl.h" + +/* 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); + +int SSL_clear(SSL *s) { + if (s->method == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_clear, SSL_R_NO_METHOD_SPECIFIED); + return 0; + } + + if (ssl_clear_bad_session(s)) { + SSL_SESSION_free(s->session); + s->session = NULL; + } + + s->hit = 0; + s->shutdown = 0; + + if (s->renegotiate) { + OPENSSL_PUT_ERROR(SSL, SSL_clear, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* SSL_clear may be called before or after the |s| is initialized in either + * accept or connect state. In the latter case, SSL_clear should preserve the + * half and reset |s->state| accordingly. */ + if (s->handshake_func != NULL) { + if (s->server) { + SSL_set_accept_state(s); + } else { + SSL_set_connect_state(s); + } + } else { + assert(s->state == 0); + } + + /* TODO(davidben): Some state on |s| 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 |s->s3| and |s->d1| so it is + * naturally reset at the right points between |SSL_new|, |SSL_clear|, and + * |ssl3_new|. */ + + s->rwstate = SSL_NOTHING; + s->rstate = SSL_ST_READ_HEADER; + + if (s->init_buf != NULL) { + BUF_MEM_free(s->init_buf); + s->init_buf = NULL; + } + + s->packet = NULL; + s->packet_length = 0; + + ssl_clear_cipher_ctx(s); + + if (s->next_proto_negotiated) { + OPENSSL_free(s->next_proto_negotiated); + s->next_proto_negotiated = NULL; + s->next_proto_negotiated_len = 0; + } + + /* The s->d1->mtu is simultaneously configuration (preserved across + * clear) and connection-specific state (gets reset). + * + * TODO(davidben): Avoid this. */ + unsigned mtu = 0; + if (s->d1 != NULL) { + mtu = s->d1->mtu; + } + + s->method->ssl_free(s); + if (!s->method->ssl_new(s)) { + return 0; + } + s->enc_method = ssl3_get_enc_method(s->version); + assert(s->enc_method != NULL); + + if (SSL_IS_DTLS(s) && (SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)) { + s->d1->mtu = mtu; + } + + s->client_version = s->version; + + return 1; +} + +SSL *SSL_new(SSL_CTX *ctx) { + SSL *s; + + if (ctx == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_new, SSL_R_NULL_SSL_CTX); + return NULL; + } + if (ctx->method == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_new, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION); + return NULL; + } + + s = (SSL *)OPENSSL_malloc(sizeof(SSL)); + if (s == NULL) { + goto err; + } + memset(s, 0, sizeof(SSL)); + + s->min_version = ctx->min_version; + s->max_version = ctx->max_version; + + s->options = ctx->options; + s->mode = ctx->mode; + s->max_cert_list = ctx->max_cert_list; + + if (ctx->cert != NULL) { + /* Earlier library versions used to copy the pointer to the CERT, not its + * contents; only when setting new parameters for the per-SSL copy, + * ssl_cert_new would be called (and the direct reference to the + * per-SSL_CTX settings would be lost, but those still were indirectly + * accessed for various purposes, and for that reason they used to be known + * as s->ctx->default_cert). Now we don't look at the SSL_CTX's CERT after + * having duplicated it once. */ + + s->cert = ssl_cert_dup(ctx->cert); + if (s->cert == NULL) { + goto err; + } + } else { + s->cert = NULL; /* Cannot really happen (see SSL_CTX_new) */ + } + + s->read_ahead = ctx->read_ahead; + s->msg_callback = ctx->msg_callback; + s->msg_callback_arg = ctx->msg_callback_arg; + s->verify_mode = ctx->verify_mode; + s->sid_ctx_length = ctx->sid_ctx_length; + assert(s->sid_ctx_length <= sizeof s->sid_ctx); + memcpy(&s->sid_ctx, &ctx->sid_ctx, sizeof(s->sid_ctx)); + s->verify_callback = ctx->default_verify_callback; + s->generate_session_id = ctx->generate_session_id; + + s->param = X509_VERIFY_PARAM_new(); + if (!s->param) { + goto err; + } + X509_VERIFY_PARAM_inherit(s->param, ctx->param); + s->quiet_shutdown = ctx->quiet_shutdown; + s->max_send_fragment = ctx->max_send_fragment; + + CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX); + s->ctx = ctx; + s->tlsext_debug_cb = 0; + s->tlsext_debug_arg = NULL; + s->tlsext_ticket_expected = 0; + CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX); + s->initial_ctx = ctx; + if (ctx->tlsext_ecpointformatlist) { + s->tlsext_ecpointformatlist = BUF_memdup( + ctx->tlsext_ecpointformatlist, ctx->tlsext_ecpointformatlist_length); + if (!s->tlsext_ecpointformatlist) { + goto err; + } + s->tlsext_ecpointformatlist_length = ctx->tlsext_ecpointformatlist_length; + } + + if (ctx->tlsext_ellipticcurvelist) { + s->tlsext_ellipticcurvelist = + BUF_memdup(ctx->tlsext_ellipticcurvelist, + ctx->tlsext_ellipticcurvelist_length * 2); + if (!s->tlsext_ellipticcurvelist) { + goto err; + } + s->tlsext_ellipticcurvelist_length = ctx->tlsext_ellipticcurvelist_length; + } + s->next_proto_negotiated = NULL; + + if (s->ctx->alpn_client_proto_list) { + s->alpn_client_proto_list = BUF_memdup(s->ctx->alpn_client_proto_list, + s->ctx->alpn_client_proto_list_len); + if (s->alpn_client_proto_list == NULL) { + goto err; + } + s->alpn_client_proto_list_len = s->ctx->alpn_client_proto_list_len; + } + + s->verify_result = X509_V_OK; + s->method = ctx->method; + + if (!s->method->ssl_new(s)) { + goto err; + } + s->enc_method = ssl3_get_enc_method(s->version); + assert(s->enc_method != NULL); + + s->references = 1; + + s->rwstate = SSL_NOTHING; + s->rstate = SSL_ST_READ_HEADER; + + CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data); + + s->psk_identity_hint = NULL; + if (ctx->psk_identity_hint) { + s->psk_identity_hint = BUF_strdup(ctx->psk_identity_hint); + if (s->psk_identity_hint == NULL) { + goto err; + } + } + s->psk_client_callback = ctx->psk_client_callback; + s->psk_server_callback = ctx->psk_server_callback; + + s->tlsext_channel_id_enabled = ctx->tlsext_channel_id_enabled; + if (ctx->tlsext_channel_id_private) { + s->tlsext_channel_id_private = EVP_PKEY_dup(ctx->tlsext_channel_id_private); + } + + s->signed_cert_timestamps_enabled = s->ctx->signed_cert_timestamps_enabled; + s->ocsp_stapling_enabled = s->ctx->ocsp_stapling_enabled; + + return s; + +err: + if (s != NULL) { + SSL_free(s); + } + OPENSSL_PUT_ERROR(SSL, SSL_new, ERR_R_MALLOC_FAILURE); + + return NULL; +} + +int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const uint8_t *sid_ctx, + unsigned int sid_ctx_len) { + if (sid_ctx_len > sizeof ctx->sid_ctx) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_session_id_context, + SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); + return 0; + } + ctx->sid_ctx_length = sid_ctx_len; + memcpy(ctx->sid_ctx, sid_ctx, sid_ctx_len); + + return 1; +} + +int SSL_set_session_id_context(SSL *ssl, const uint8_t *sid_ctx, + unsigned int sid_ctx_len) { + if (sid_ctx_len > SSL_MAX_SID_CTX_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_set_session_id_context, + SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); + return 0; + } + ssl->sid_ctx_length = sid_ctx_len; + memcpy(ssl->sid_ctx, sid_ctx, sid_ctx_len); + + return 1; +} + +int SSL_CTX_set_generate_session_id(SSL_CTX *ctx, GEN_SESSION_CB cb) { + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + ctx->generate_session_id = cb; + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + return 1; +} + +int SSL_set_generate_session_id(SSL *ssl, GEN_SESSION_CB cb) { + CRYPTO_w_lock(CRYPTO_LOCK_SSL); + ssl->generate_session_id = cb; + CRYPTO_w_unlock(CRYPTO_LOCK_SSL); + return 1; +} + +int SSL_has_matching_session_id(const SSL *ssl, const uint8_t *id, + unsigned int id_len) { + /* A quick examination of SSL_SESSION_hash and SSL_SESSION_cmp shows how we + * can "construct" a session to give us the desired check - ie. to find if + * there's a session in the hash table that would conflict with any new + * session built out of this id/id_len and the ssl_version in use by this + * SSL. */ + SSL_SESSION r, *p; + + if (id_len > sizeof r.session_id) { + return 0; + } + + r.ssl_version = ssl->version; + r.session_id_length = id_len; + memcpy(r.session_id, id, id_len); + + CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); + p = lh_SSL_SESSION_retrieve(ssl->ctx->sessions, &r); + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); + return p != NULL; +} + +int SSL_CTX_set_purpose(SSL_CTX *s, int purpose) { + return X509_VERIFY_PARAM_set_purpose(s->param, purpose); +} + +int SSL_set_purpose(SSL *s, int purpose) { + return X509_VERIFY_PARAM_set_purpose(s->param, purpose); +} + +int SSL_CTX_set_trust(SSL_CTX *s, int trust) { + return X509_VERIFY_PARAM_set_trust(s->param, trust); +} + +int SSL_set_trust(SSL *s, int trust) { + return X509_VERIFY_PARAM_set_trust(s->param, trust); +} + +int SSL_CTX_set1_param(SSL_CTX *ctx, X509_VERIFY_PARAM *vpm) { + return X509_VERIFY_PARAM_set1(ctx->param, vpm); +} + +int SSL_set1_param(SSL *ssl, X509_VERIFY_PARAM *vpm) { + return X509_VERIFY_PARAM_set1(ssl->param, vpm); +} + +void ssl_cipher_preference_list_free( + struct ssl_cipher_preference_list_st *cipher_list) { + sk_SSL_CIPHER_free(cipher_list->ciphers); + OPENSSL_free(cipher_list->in_group_flags); + OPENSSL_free(cipher_list); +} + +struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_dup( + struct ssl_cipher_preference_list_st *cipher_list) { + struct ssl_cipher_preference_list_st *ret = NULL; + size_t n = sk_SSL_CIPHER_num(cipher_list->ciphers); + + ret = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st)); + if (!ret) { + goto err; + } + + ret->ciphers = NULL; + ret->in_group_flags = NULL; + ret->ciphers = sk_SSL_CIPHER_dup(cipher_list->ciphers); + if (!ret->ciphers) { + goto err; + } + ret->in_group_flags = BUF_memdup(cipher_list->in_group_flags, n); + if (!ret->in_group_flags) { + goto err; + } + + return ret; + +err: + if (ret && ret->ciphers) { + sk_SSL_CIPHER_free(ret->ciphers); + } + if (ret) { + OPENSSL_free(ret); + } + return NULL; +} + +struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_from_ciphers( + STACK_OF(SSL_CIPHER) * ciphers) { + struct ssl_cipher_preference_list_st *ret = NULL; + size_t n = sk_SSL_CIPHER_num(ciphers); + + ret = OPENSSL_malloc(sizeof(struct ssl_cipher_preference_list_st)); + if (!ret) { + goto err; + } + ret->ciphers = NULL; + ret->in_group_flags = NULL; + ret->ciphers = sk_SSL_CIPHER_dup(ciphers); + if (!ret->ciphers) { + goto err; + } + ret->in_group_flags = OPENSSL_malloc(n); + if (!ret->in_group_flags) { + goto err; + } + memset(ret->in_group_flags, 0, n); + return ret; + +err: + if (ret && ret->ciphers) { + sk_SSL_CIPHER_free(ret->ciphers); + } + if (ret) { + OPENSSL_free(ret); + } + return NULL; +} + +X509_VERIFY_PARAM *SSL_CTX_get0_param(SSL_CTX *ctx) { return ctx->param; } + +X509_VERIFY_PARAM *SSL_get0_param(SSL *ssl) { return ssl->param; } + +void SSL_certs_clear(SSL *s) { ssl_cert_clear_certs(s->cert); } + +void SSL_free(SSL *s) { + int i; + + if (s == NULL) { + return; + } + + i = CRYPTO_add(&s->references, -1, CRYPTO_LOCK_SSL); + if (i > 0) { + return; + } + + if (s->param) { + X509_VERIFY_PARAM_free(s->param); + } + + CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL, s, &s->ex_data); + + if (s->bbio != NULL) { + /* If the buffering BIO is in place, pop it off */ + if (s->bbio == s->wbio) { + s->wbio = BIO_pop(s->wbio); + } + BIO_free(s->bbio); + s->bbio = NULL; + } + + if (s->rbio != NULL) { + BIO_free_all(s->rbio); + } + + if (s->wbio != NULL && s->wbio != s->rbio) { + BIO_free_all(s->wbio); + } + + if (s->init_buf != NULL) { + BUF_MEM_free(s->init_buf); + } + + /* add extra stuff */ + if (s->cipher_list != NULL) { + ssl_cipher_preference_list_free(s->cipher_list); + } + if (s->cipher_list_by_id != NULL) { + sk_SSL_CIPHER_free(s->cipher_list_by_id); + } + + if (s->session != NULL) { + ssl_clear_bad_session(s); + SSL_SESSION_free(s->session); + } + + ssl_clear_cipher_ctx(s); + + if (s->cert != NULL) { + ssl_cert_free(s->cert); + } + + if (s->tlsext_hostname) { + OPENSSL_free(s->tlsext_hostname); + } + if (s->initial_ctx) { + SSL_CTX_free(s->initial_ctx); + } + if (s->tlsext_ecpointformatlist) { + OPENSSL_free(s->tlsext_ecpointformatlist); + } + if (s->tlsext_ellipticcurvelist) { + OPENSSL_free(s->tlsext_ellipticcurvelist); + } + if (s->alpn_client_proto_list) { + OPENSSL_free(s->alpn_client_proto_list); + } + if (s->tlsext_channel_id_private) { + EVP_PKEY_free(s->tlsext_channel_id_private); + } + if (s->psk_identity_hint) { + OPENSSL_free(s->psk_identity_hint); + } + if (s->client_CA != NULL) { + sk_X509_NAME_pop_free(s->client_CA, X509_NAME_free); + } + if (s->next_proto_negotiated) { + OPENSSL_free(s->next_proto_negotiated); + } + if (s->srtp_profiles) { + sk_SRTP_PROTECTION_PROFILE_free(s->srtp_profiles); + } + + if (s->method != NULL) { + s->method->ssl_free(s); + } + if (s->ctx) { + SSL_CTX_free(s->ctx); + } + + OPENSSL_free(s); +} + +void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio) { + /* If the output buffering BIO is still in place, remove it. */ + if (s->bbio != NULL) { + if (s->wbio == s->bbio) { + s->wbio = s->wbio->next_bio; + s->bbio->next_bio = NULL; + } + } + + if (s->rbio != NULL && s->rbio != rbio) { + BIO_free_all(s->rbio); + } + if (s->wbio != NULL && s->wbio != wbio && s->rbio != s->wbio) { + BIO_free_all(s->wbio); + } + s->rbio = rbio; + s->wbio = wbio; +} + +BIO *SSL_get_rbio(const SSL *s) { return s->rbio; } + +BIO *SSL_get_wbio(const SSL *s) { return s->wbio; } + +int SSL_get_fd(const SSL *s) { return SSL_get_rfd(s); } + +int SSL_get_rfd(const SSL *s) { + int ret = -1; + BIO *b, *r; + + b = SSL_get_rbio(s); + r = BIO_find_type(b, BIO_TYPE_DESCRIPTOR); + if (r != NULL) { + BIO_get_fd(r, &ret); + } + return ret; +} + +int SSL_get_wfd(const SSL *s) { + int ret = -1; + BIO *b, *r; + + b = SSL_get_wbio(s); + r = BIO_find_type(b, BIO_TYPE_DESCRIPTOR); + if (r != NULL) { + BIO_get_fd(r, &ret); + } + + return ret; +} + +int SSL_set_fd(SSL *s, int fd) { + int ret = 0; + BIO *bio = NULL; + + bio = BIO_new(BIO_s_fd()); + + if (bio == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_set_fd, ERR_R_BUF_LIB); + goto err; + } + BIO_set_fd(bio, fd, BIO_NOCLOSE); + SSL_set_bio(s, bio, bio); + ret = 1; + +err: + return ret; +} + +int SSL_set_wfd(SSL *s, int fd) { + int ret = 0; + BIO *bio = NULL; + + if (s->rbio == NULL || BIO_method_type(s->rbio) != BIO_TYPE_FD || + (int)BIO_get_fd(s->rbio, NULL) != fd) { + bio = BIO_new(BIO_s_fd()); + + if (bio == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_set_wfd, ERR_R_BUF_LIB); + goto err; + } + BIO_set_fd(bio, fd, BIO_NOCLOSE); + SSL_set_bio(s, SSL_get_rbio(s), bio); + } else { + SSL_set_bio(s, SSL_get_rbio(s), SSL_get_rbio(s)); + } + + ret = 1; + +err: + return ret; +} + +int SSL_set_rfd(SSL *s, int fd) { + int ret = 0; + BIO *bio = NULL; + + if (s->wbio == NULL || BIO_method_type(s->wbio) != BIO_TYPE_FD || + (int)BIO_get_fd(s->wbio, NULL) != fd) { + bio = BIO_new(BIO_s_fd()); + + if (bio == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_set_rfd, ERR_R_BUF_LIB); + goto err; + } + BIO_set_fd(bio, fd, BIO_NOCLOSE); + SSL_set_bio(s, bio, SSL_get_wbio(s)); + } else { + SSL_set_bio(s, SSL_get_wbio(s), SSL_get_wbio(s)); + } + ret = 1; + +err: + return ret; +} + +/* return length of latest Finished message we sent, copy to 'buf' */ +size_t SSL_get_finished(const SSL *s, void *buf, size_t count) { + size_t ret = 0; + + if (s->s3 != NULL) { + ret = s->s3->tmp.finish_md_len; + if (count > ret) { + count = ret; + } + memcpy(buf, s->s3->tmp.finish_md, count); + } + + return ret; +} + +/* return length of latest Finished message we expected, copy to 'buf' */ +size_t SSL_get_peer_finished(const SSL *s, void *buf, size_t count) { + size_t ret = 0; + + if (s->s3 != NULL) { + ret = s->s3->tmp.peer_finish_md_len; + if (count > ret) { + count = ret; + } + memcpy(buf, s->s3->tmp.peer_finish_md, count); + } + + return ret; +} + +int SSL_get_verify_mode(const SSL *s) { return s->verify_mode; } + +int SSL_get_verify_depth(const SSL *s) { + return X509_VERIFY_PARAM_get_depth(s->param); +} + +int (*SSL_get_verify_callback(const SSL *s))(int, X509_STORE_CTX *) { + return s->verify_callback; +} + +int SSL_CTX_get_verify_mode(const SSL_CTX *ctx) { return ctx->verify_mode; } + +int SSL_CTX_get_verify_depth(const SSL_CTX *ctx) { + return X509_VERIFY_PARAM_get_depth(ctx->param); +} + +int (*SSL_CTX_get_verify_callback(const SSL_CTX *ctx))(int, X509_STORE_CTX *) { + return ctx->default_verify_callback; +} + +void SSL_set_verify(SSL *s, int mode, + int (*callback)(int ok, X509_STORE_CTX *ctx)) { + s->verify_mode = mode; + if (callback != NULL) { + s->verify_callback = callback; + } +} + +void SSL_set_verify_depth(SSL *s, int depth) { + X509_VERIFY_PARAM_set_depth(s->param, depth); +} + +void SSL_set_read_ahead(SSL *s, int yes) { s->read_ahead = yes; } + +int SSL_get_read_ahead(const SSL *s) { return s->read_ahead; } + +int SSL_pending(const SSL *s) { + /* SSL_pending cannot work properly if read-ahead is enabled + * (SSL_[CTX_]ctrl(..., SSL_CTRL_SET_READ_AHEAD, 1, NULL)), and it is + * impossible to fix since SSL_pending cannot report errors that may be + * observed while scanning the new data. (Note that SSL_pending() is often + * used as a boolean value, so we'd better not return -1.). */ + return s->method->ssl_pending(s); +} + +X509 *SSL_get_peer_certificate(const SSL *s) { + X509 *r; + + if (s == NULL || s->session == NULL) { + r = NULL; + } else { + r = s->session->peer; + } + + if (r == NULL) { + return NULL; + } + + return X509_up_ref(r); +} + +STACK_OF(X509) * SSL_get_peer_cert_chain(const SSL *s) { + STACK_OF(X509) * r; + + if (s == NULL || s->session == NULL || s->session->sess_cert == NULL) { + r = NULL; + } else { + r = s->session->sess_cert->cert_chain; + } + + /* If we are a client, cert_chain includes the peer's own certificate; if we + * are a server, it does not. */ + return r; +} + +/* Fix this so it checks all the valid key/cert options */ +int SSL_CTX_check_private_key(const SSL_CTX *ctx) { + if (ctx == NULL || ctx->cert == NULL || ctx->cert->key->x509 == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_check_private_key, + SSL_R_NO_CERTIFICATE_ASSIGNED); + return 0; + } + + if (ctx->cert->key->privatekey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_check_private_key, + SSL_R_NO_PRIVATE_KEY_ASSIGNED); + return 0; + } + + return X509_check_private_key(ctx->cert->key->x509, + ctx->cert->key->privatekey); +} + +/* Fix this function so that it takes an optional type parameter */ +int SSL_check_private_key(const SSL *ssl) { + if (ssl == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_check_private_key, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (ssl->cert == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_check_private_key, + SSL_R_NO_CERTIFICATE_ASSIGNED); + return 0; + } + + if (ssl->cert->key->x509 == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_check_private_key, + SSL_R_NO_CERTIFICATE_ASSIGNED); + return 0; + } + + if (ssl->cert->key->privatekey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_check_private_key, + SSL_R_NO_PRIVATE_KEY_ASSIGNED); + return 0; + } + + return X509_check_private_key(ssl->cert->key->x509, + ssl->cert->key->privatekey); +} + +int SSL_accept(SSL *s) { + if (s->handshake_func == 0) { + /* Not properly initialized yet */ + SSL_set_accept_state(s); + } + + if (s->handshake_func != s->method->ssl_accept) { + OPENSSL_PUT_ERROR(SSL, SSL_connect, ERR_R_INTERNAL_ERROR); + return -1; + } + + return s->handshake_func(s); +} + +int SSL_connect(SSL *s) { + if (s->handshake_func == 0) { + /* Not properly initialized yet */ + SSL_set_connect_state(s); + } + + if (s->handshake_func != s->method->ssl_connect) { + OPENSSL_PUT_ERROR(SSL, SSL_connect, ERR_R_INTERNAL_ERROR); + return -1; + } + + return s->handshake_func(s); +} + +long SSL_get_default_timeout(const SSL *s) { + return SSL_DEFAULT_SESSION_TIMEOUT; +} + +int SSL_read(SSL *s, void *buf, int num) { + if (s->handshake_func == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_read, SSL_R_UNINITIALIZED); + return -1; + } + + if (s->shutdown & SSL_RECEIVED_SHUTDOWN) { + s->rwstate = SSL_NOTHING; + return 0; + } + + return s->method->ssl_read(s, buf, num); +} + +int SSL_peek(SSL *s, void *buf, int num) { + if (s->handshake_func == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_peek, SSL_R_UNINITIALIZED); + return -1; + } + + if (s->shutdown & SSL_RECEIVED_SHUTDOWN) { + return 0; + } + + return s->method->ssl_peek(s, buf, num); +} + +int SSL_write(SSL *s, const void *buf, int num) { + if (s->handshake_func == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_write, SSL_R_UNINITIALIZED); + return -1; + } + + if (s->shutdown & SSL_SENT_SHUTDOWN) { + s->rwstate = SSL_NOTHING; + OPENSSL_PUT_ERROR(SSL, SSL_write, SSL_R_PROTOCOL_IS_SHUTDOWN); + return -1; + } + + return s->method->ssl_write(s, buf, num); +} + +int SSL_shutdown(SSL *s) { + /* Note that this function behaves differently from what one might expect. + * Return values are 0 for no success (yet), 1 for success; but calling it + * once is usually not enough, even if blocking I/O is used (see + * ssl3_shutdown). */ + + if (s->handshake_func == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_shutdown, SSL_R_UNINITIALIZED); + return -1; + } + + if (!SSL_in_init(s)) { + return s->method->ssl_shutdown(s); + } + + return 1; +} + +int SSL_renegotiate(SSL *s) { + if (s->renegotiate == 0) { + s->renegotiate = 1; + } + + s->new_session = 1; + return s->method->ssl_renegotiate(s); +} + +int SSL_renegotiate_abbreviated(SSL *s) { + if (s->renegotiate == 0) { + s->renegotiate = 1; + } + + s->new_session = 0; + return s->method->ssl_renegotiate(s); +} + +int SSL_renegotiate_pending(SSL *s) { + /* becomes true when negotiation is requested; false again once a handshake + * has finished */ + return s->renegotiate != 0; +} + +long SSL_ctrl(SSL *s, int cmd, long larg, void *parg) { + long l; + + switch (cmd) { + case SSL_CTRL_GET_READ_AHEAD: + return s->read_ahead; + + case SSL_CTRL_SET_READ_AHEAD: + l = s->read_ahead; + s->read_ahead = larg; + return l; + + case SSL_CTRL_SET_MSG_CALLBACK_ARG: + s->msg_callback_arg = parg; + return 1; + + case SSL_CTRL_OPTIONS: + return s->options |= larg; + + case SSL_CTRL_CLEAR_OPTIONS: + return s->options &= ~larg; + + case SSL_CTRL_MODE: + return s->mode |= larg; + + case SSL_CTRL_CLEAR_MODE: + return s->mode &= ~larg; + + case SSL_CTRL_GET_MAX_CERT_LIST: + return s->max_cert_list; + + case SSL_CTRL_SET_MAX_CERT_LIST: + l = s->max_cert_list; + s->max_cert_list = larg; + return l; + + case SSL_CTRL_SET_MTU: + if (larg < (long)dtls1_min_mtu()) { + return 0; + } + if (SSL_IS_DTLS(s)) { + s->d1->mtu = larg; + return larg; + } + return 0; + + case SSL_CTRL_SET_MAX_SEND_FRAGMENT: + if (larg < 512 || larg > SSL3_RT_MAX_PLAIN_LENGTH) { + return 0; + } + s->max_send_fragment = larg; + return 1; + + case SSL_CTRL_GET_RI_SUPPORT: + if (s->s3) { + return s->s3->send_connection_binding; + } + return 0; + + case SSL_CTRL_CERT_FLAGS: + return s->cert->cert_flags |= larg; + + case SSL_CTRL_CLEAR_CERT_FLAGS: + return s->cert->cert_flags &= ~larg; + + case SSL_CTRL_GET_RAW_CIPHERLIST: + if (parg) { + if (s->cert->ciphers_raw == NULL) { + return 0; + } + *(uint8_t **)parg = s->cert->ciphers_raw; + return (int)s->cert->ciphers_rawlen; + } + + /* Passing a NULL |parg| returns the size of a single + * cipher suite value. */ + return 2; + + default: + return s->method->ssl_ctrl(s, cmd, larg, parg); + } +} + +long SSL_callback_ctrl(SSL *s, int cmd, void (*fp)(void)) { + switch (cmd) { + case SSL_CTRL_SET_MSG_CALLBACK: + s->msg_callback = + (void (*)(int write_p, int version, int content_type, const void *buf, + size_t len, SSL *ssl, void *arg))(fp); + return 1; + + default: + return s->method->ssl_callback_ctrl(s, cmd, fp); + } +} + +LHASH_OF(SSL_SESSION) *SSL_CTX_sessions(SSL_CTX *ctx) { return ctx->sessions; } + +long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) { + long l; + + switch (cmd) { + case SSL_CTRL_GET_READ_AHEAD: + return ctx->read_ahead; + + case SSL_CTRL_SET_READ_AHEAD: + l = ctx->read_ahead; + ctx->read_ahead = larg; + return l; + + case SSL_CTRL_SET_MSG_CALLBACK_ARG: + ctx->msg_callback_arg = parg; + return 1; + + case SSL_CTRL_GET_MAX_CERT_LIST: + return ctx->max_cert_list; + + case SSL_CTRL_SET_MAX_CERT_LIST: + l = ctx->max_cert_list; + ctx->max_cert_list = larg; + return l; + + case SSL_CTRL_SET_SESS_CACHE_SIZE: + l = ctx->session_cache_size; + ctx->session_cache_size = larg; + return l; + + case SSL_CTRL_GET_SESS_CACHE_SIZE: + return ctx->session_cache_size; + + case SSL_CTRL_SET_SESS_CACHE_MODE: + l = ctx->session_cache_mode; + ctx->session_cache_mode = larg; + return l; + + case SSL_CTRL_GET_SESS_CACHE_MODE: + return ctx->session_cache_mode; + + case SSL_CTRL_SESS_NUMBER: + return lh_SSL_SESSION_num_items(ctx->sessions); + + case SSL_CTRL_SESS_CONNECT: + return ctx->stats.sess_connect; + + case SSL_CTRL_SESS_CONNECT_GOOD: + return ctx->stats.sess_connect_good; + + case SSL_CTRL_SESS_CONNECT_RENEGOTIATE: + return ctx->stats.sess_connect_renegotiate; + + case SSL_CTRL_SESS_ACCEPT: + return ctx->stats.sess_accept; + + case SSL_CTRL_SESS_ACCEPT_GOOD: + return ctx->stats.sess_accept_good; + + case SSL_CTRL_SESS_ACCEPT_RENEGOTIATE: + return ctx->stats.sess_accept_renegotiate; + + case SSL_CTRL_SESS_HIT: + return ctx->stats.sess_hit; + + case SSL_CTRL_SESS_CB_HIT: + return ctx->stats.sess_cb_hit; + + case SSL_CTRL_SESS_MISSES: + return ctx->stats.sess_miss; + + case SSL_CTRL_SESS_TIMEOUTS: + return ctx->stats.sess_timeout; + + case SSL_CTRL_SESS_CACHE_FULL: + return ctx->stats.sess_cache_full; + + case SSL_CTRL_OPTIONS: + return ctx->options |= larg; + + case SSL_CTRL_CLEAR_OPTIONS: + return ctx->options &= ~larg; + + case SSL_CTRL_MODE: + return ctx->mode |= larg; + + case SSL_CTRL_CLEAR_MODE: + return ctx->mode &= ~larg; + + case SSL_CTRL_SET_MAX_SEND_FRAGMENT: + if (larg < 512 || larg > SSL3_RT_MAX_PLAIN_LENGTH) { + return 0; + } + ctx->max_send_fragment = larg; + return 1; + + case SSL_CTRL_CERT_FLAGS: + return ctx->cert->cert_flags |= larg; + + case SSL_CTRL_CLEAR_CERT_FLAGS: + return ctx->cert->cert_flags &= ~larg; + + default: + return ctx->method->ssl_ctx_ctrl(ctx, cmd, larg, parg); + } +} + +long SSL_CTX_callback_ctrl(SSL_CTX *ctx, int cmd, void (*fp)(void)) { + switch (cmd) { + case SSL_CTRL_SET_MSG_CALLBACK: + ctx->msg_callback = + (void (*)(int write_p, int version, int content_type, const void *buf, + size_t len, SSL *ssl, void *arg))(fp); + return 1; + + default: + return ctx->method->ssl_ctx_callback_ctrl(ctx, cmd, fp); + } +} + +int ssl_cipher_id_cmp(const void *in_a, const void *in_b) { + long l; + const SSL_CIPHER *a = in_a; + const SSL_CIPHER *b = in_b; + const long a_id = a->id; + const long b_id = b->id; + + l = a_id - b_id; + if (l == 0L) { + return 0; + } else { + return (l > 0) ? 1 : -1; + } +} + +int ssl_cipher_ptr_id_cmp(const SSL_CIPHER **ap, const SSL_CIPHER **bp) { + long l; + const long a_id = (*ap)->id; + const long b_id = (*bp)->id; + + l = a_id - b_id; + if (l == 0) { + return 0; + } else { + return (l > 0) ? 1 : -1; + } +} + +/* return a STACK of the ciphers available for the SSL and in order of + * preference */ +STACK_OF(SSL_CIPHER) * SSL_get_ciphers(const SSL *s) { + if (s == NULL) { + return NULL; + } + + if (s->cipher_list != NULL) { + return s->cipher_list->ciphers; + } + + if (s->version >= TLS1_1_VERSION && s->ctx != NULL && + s->ctx->cipher_list_tls11 != NULL) { + return s->ctx->cipher_list_tls11->ciphers; + } + + if (s->ctx != NULL && s->ctx->cipher_list != NULL) { + return s->ctx->cipher_list->ciphers; + } + + return NULL; +} + +/* return a STACK of the ciphers available for the SSL and in order of + * algorithm id */ +STACK_OF(SSL_CIPHER) * ssl_get_ciphers_by_id(SSL *s) { + if (s == NULL) { + return NULL; + } + + if (s->cipher_list_by_id != NULL) { + return s->cipher_list_by_id; + } + + if (s->ctx != NULL && s->ctx->cipher_list_by_id != NULL) { + return s->ctx->cipher_list_by_id; + } + + return NULL; +} + +/* The old interface to get the same thing as SSL_get_ciphers() */ +const char *SSL_get_cipher_list(const SSL *s, int n) { + const SSL_CIPHER *c; + STACK_OF(SSL_CIPHER) * sk; + + if (s == NULL) { + return NULL; + } + + sk = SSL_get_ciphers(s); + if (sk == NULL || n < 0 || (size_t)n >= sk_SSL_CIPHER_num(sk)) { + return NULL; + } + + c = sk_SSL_CIPHER_value(sk, n); + if (c == NULL) { + return NULL; + } + + return c->name; +} + +/* specify the ciphers to be used by default by the SSL_CTX */ +int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) { + STACK_OF(SSL_CIPHER) *sk; + + sk = ssl_create_cipher_list(ctx->method, &ctx->cipher_list, + &ctx->cipher_list_by_id, str, ctx->cert); + /* ssl_create_cipher_list may return an empty stack if it was unable to find + * a cipher matching the given rule string (for example if the rule string + * specifies a cipher which has been disabled). This is not an error as far + * as ssl_create_cipher_list is concerned, and hence ctx->cipher_list and + * ctx->cipher_list_by_id has been updated. */ + if (sk == NULL) { + return 0; + } else if (sk_SSL_CIPHER_num(sk) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_cipher_list, SSL_R_NO_CIPHER_MATCH); + return 0; + } + + return 1; +} + +int SSL_CTX_set_cipher_list_tls11(SSL_CTX *ctx, const char *str) { + STACK_OF(SSL_CIPHER) *sk; + + sk = ssl_create_cipher_list(ctx->method, &ctx->cipher_list_tls11, NULL, str, + ctx->cert); + if (sk == NULL) { + return 0; + } else if (sk_SSL_CIPHER_num(sk) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_set_cipher_list_tls11, + SSL_R_NO_CIPHER_MATCH); + return 0; + } + + return 1; +} + +/* specify the ciphers to be used by the SSL */ +int SSL_set_cipher_list(SSL *s, const char *str) { + STACK_OF(SSL_CIPHER) *sk; + + sk = ssl_create_cipher_list(s->ctx->method, &s->cipher_list, + &s->cipher_list_by_id, str, s->cert); + + /* see comment in SSL_CTX_set_cipher_list */ + if (sk == NULL) { + return 0; + } else if (sk_SSL_CIPHER_num(sk) == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_set_cipher_list, SSL_R_NO_CIPHER_MATCH); + return 0; + } + + return 1; +} + +int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, uint8_t *p) { + size_t i; + const SSL_CIPHER *c; + CERT *ct = s->cert; + uint8_t *q; + /* Set disabled masks for this session */ + ssl_set_client_disabled(s); + + if (sk == NULL) { + return 0; + } + q = p; + + for (i = 0; i < sk_SSL_CIPHER_num(sk); i++) { + c = sk_SSL_CIPHER_value(sk, i); + /* Skip disabled ciphers */ + if (c->algorithm_ssl & ct->mask_ssl || + c->algorithm_mkey & ct->mask_k || + c->algorithm_auth & ct->mask_a) { + continue; + } + s2n(ssl3_get_cipher_value(c), p); + } + + /* If all ciphers were disabled, return the error to the caller. */ + if (p == q) { + return 0; + } + + /* Add SCSVs. */ + if (!s->renegotiate) { + s2n(SSL3_CK_SCSV & 0xffff, p); + } + + if (s->fallback_scsv) { + s2n(SSL3_CK_FALLBACK_SCSV & 0xffff, p); + } + + return p - q; +} + +STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs) { + CBS cipher_suites = *cbs; + const SSL_CIPHER *c; + STACK_OF(SSL_CIPHER) * sk; + + if (s->s3) { + s->s3->send_connection_binding = 0; + } + + if (CBS_len(&cipher_suites) % 2 != 0) { + OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, + SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST); + return NULL; + } + + sk = sk_SSL_CIPHER_new_null(); + if (sk == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CBS_stow(&cipher_suites, &s->cert->ciphers_raw, + &s->cert->ciphers_rawlen)) { + OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, 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_bytes_to_cipher_list, ERR_R_INTERNAL_ERROR); + goto err; + } + + /* Check for SCSV. */ + if (s->s3 && cipher_suite == (SSL3_CK_SCSV & 0xffff)) { + /* SCSV is fatal if renegotiating. */ + if (s->renegotiate) { + OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, + SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); + goto err; + } + s->s3->send_connection_binding = 1; + continue; + } + + /* Check for FALLBACK_SCSV. */ + if (s->s3 && cipher_suite == (SSL3_CK_FALLBACK_SCSV & 0xffff)) { + uint16_t max_version = ssl3_get_max_server_version(s); + if (SSL_IS_DTLS(s) ? (uint16_t)s->version > max_version + : (uint16_t)s->version < max_version) { + OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, + SSL_R_INAPPROPRIATE_FALLBACK); + ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_INAPPROPRIATE_FALLBACK); + goto err; + } + continue; + } + + c = ssl3_get_cipher_by_value(cipher_suite); + if (c != NULL && !sk_SSL_CIPHER_push(sk, c)) { + OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + return sk; + +err: + if (sk != NULL) { + sk_SSL_CIPHER_free(sk); + } + return NULL; +} + + +/* return a servername extension value if provided in Client Hello, or NULL. So + * far, only host_name types are defined (RFC 3546). */ +const char *SSL_get_servername(const SSL *s, const int type) { + if (type != TLSEXT_NAMETYPE_host_name) { + return NULL; + } + + return s->session && !s->tlsext_hostname ? s->session->tlsext_hostname + : s->tlsext_hostname; +} + +int SSL_get_servername_type(const SSL *s) { + if (s->session && + (!s->tlsext_hostname ? s->session->tlsext_hostname : s->tlsext_hostname)) { + return TLSEXT_NAMETYPE_host_name; + } + + return -1; +} + +void SSL_CTX_enable_signed_cert_timestamps(SSL_CTX *ctx) { + ctx->signed_cert_timestamps_enabled = 1; +} + +int SSL_enable_signed_cert_timestamps(SSL *ssl) { + ssl->signed_cert_timestamps_enabled = 1; + return 1; +} + +void SSL_CTX_enable_ocsp_stapling(SSL_CTX *ctx) { + ctx->ocsp_stapling_enabled = 1; +} + +int SSL_enable_ocsp_stapling(SSL *ssl) { + ssl->ocsp_stapling_enabled = 1; + return 1; +} + +void SSL_get0_signed_cert_timestamp_list(const SSL *ssl, const uint8_t **out, + size_t *out_len) { + SSL_SESSION *session = ssl->session; + + *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->session; + + *out_len = 0; + *out = NULL; + if (ssl->server || !session || !session->ocsp_response) { + return; + } + *out = session->ocsp_response; + *out_len = session->ocsp_response_length; +} + +/* SSL_select_next_proto implements the standard protocol selection. It is + * expected that this function is called from the callback set by + * SSL_CTX_set_next_proto_select_cb. + * + * The protocol data is assumed to be a vector of 8-bit, length prefixed byte + * strings. The length byte itself is not included in the length. A byte + * string of length 0 is invalid. No byte string may be truncated. + * + * The current, but experimental algorithm for selecting the protocol is: + * + * 1) If the server doesn't support NPN then this is indicated to the + * callback. In this case, the client application has to abort the connection + * or have a default application level protocol. + * + * 2) If the server supports NPN, but advertises an empty list then the + * client selects the first protcol in its list, but indicates via the + * API that this fallback case was enacted. + * + * 3) Otherwise, the client finds the first protocol in the server's list + * that it supports and selects this protocol. This is because it's + * assumed that the server has better information about which protocol + * a client should use. + * + * 4) If the client doesn't support any of the server's advertised + * protocols, then this is treated the same as case 2. + * + * It returns either + * OPENSSL_NPN_NEGOTIATED if a common protocol was found, or + * OPENSSL_NPN_NO_OVERLAP if the fallback case was reached. + */ +int SSL_select_next_proto(uint8_t **out, uint8_t *outlen, const uint8_t *server, + unsigned int server_len, const uint8_t *client, + unsigned int 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] && + 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; + *outlen = result[0]; + return status; +} + +/* SSL_get0_next_proto_negotiated sets *data and *len to point to the client's + * requested protocol for this connection and returns 0. If the client didn't + * request any protocol, then *data is set to NULL. + * + * Note that the client can request any protocol it chooses. The value returned + * from this function need not be a member of the list of supported protocols + * provided by the callback. */ +void SSL_get0_next_proto_negotiated(const SSL *s, const uint8_t **data, + unsigned *len) { + *data = s->next_proto_negotiated; + if (!*data) { + *len = 0; + } else { + *len = s->next_proto_negotiated_len; + } +} + +/* SSL_CTX_set_next_protos_advertised_cb sets a callback that is called when a + * TLS server needs a list of supported protocols for Next Protocol + * Negotiation. The returned list must be in wire format. The list is returned + * by setting |out| to point to it and |outlen| to its length. This memory will + * not be modified, but one should assume that the SSL* keeps a reference to + * it. + * + * The callback should return SSL_TLSEXT_ERR_OK if it wishes to advertise. + * Otherwise, no such extension will be included in the ServerHello. */ +void SSL_CTX_set_next_protos_advertised_cb( + SSL_CTX *ctx, + int (*cb)(SSL *ssl, const uint8_t **out, unsigned int *outlen, void *arg), + void *arg) { + ctx->next_protos_advertised_cb = cb; + ctx->next_protos_advertised_cb_arg = arg; +} + +/* SSL_CTX_set_next_proto_select_cb sets a callback that is called when a + * client needs to select a protocol from the server's provided list. |out| + * must be set to point to the selected protocol (which may be within |in|). + * The length of the protocol name must be written into |outlen|. The server's + * advertised protocols are provided in |in| and |inlen|. The callback can + * assume that |in| is syntactically valid. + * + * The client must select a protocol. It is fatal to the connection if this + * callback returns a value other than SSL_TLSEXT_ERR_OK. + */ +void SSL_CTX_set_next_proto_select_cb( + SSL_CTX *ctx, int (*cb)(SSL *s, uint8_t **out, uint8_t *outlen, + const uint8_t *in, unsigned int inlen, void *arg), + void *arg) { + ctx->next_proto_select_cb = cb; + ctx->next_proto_select_cb_arg = arg; +} + +/* SSL_CTX_set_alpn_protos sets the ALPN protocol list on |ctx| to |protos|. + * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit + * length-prefixed strings). + * + * Returns 0 on success. */ +int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const uint8_t *protos, + unsigned protos_len) { + if (ctx->alpn_client_proto_list) { + 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; +} + +/* SSL_set_alpn_protos sets the ALPN protocol list on |ssl| to |protos|. + * |protos| must be in wire-format (i.e. a series of non-empty, 8-bit + * length-prefixed strings). + * + * Returns 0 on success. */ +int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, unsigned protos_len) { + if (ssl->alpn_client_proto_list) { + 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; +} + +/* SSL_CTX_set_alpn_select_cb sets a callback function on |ctx| that is called + * during ClientHello processing in order to select an ALPN protocol from the + * client's list of offered protocols. */ +void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, + int (*cb)(SSL *ssl, const uint8_t **out, + uint8_t *outlen, const uint8_t *in, + unsigned int inlen, void *arg), + void *arg) { + ctx->alpn_select_cb = cb; + ctx->alpn_select_cb_arg = arg; +} + +/* SSL_get0_alpn_selected gets the selected ALPN protocol (if any) from |ssl|. + * On return it sets |*data| to point to |*len| bytes of protocol name (not + * including the leading length-prefix byte). If the server didn't respond with + * a negotiated protocol then |*len| will be zero. */ +void SSL_get0_alpn_selected(const SSL *ssl, const uint8_t **data, + unsigned *len) { + *data = NULL; + if (ssl->s3) { + *data = ssl->s3->alpn_selected; + } + if (*data == NULL) { + *len = 0; + } else { + *len = ssl->s3->alpn_selected_len; + } +} + +int SSL_export_keying_material(SSL *s, uint8_t *out, size_t olen, + const char *label, size_t llen, const uint8_t *p, + size_t plen, int use_context) { + if (s->version < TLS1_VERSION) { + return -1; + } + + return s->enc_method->export_keying_material(s, out, olen, label, llen, p, + plen, use_context); +} + +static uint32_t ssl_session_hash(const SSL_SESSION *a) { + uint32_t hash = + ((uint32_t)a->session_id[0]) || + ((uint32_t)a->session_id[1] << 8) || + ((uint32_t)a->session_id[2] << 16) || + ((uint32_t)a->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 memcmp(a->session_id, b->session_id, a->session_id_length); +} + +SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) { + SSL_CTX *ret = NULL; + + if (meth == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, SSL_R_NULL_SSL_METHOD_PASSED); + return NULL; + } + + if (SSL_get_ex_data_X509_STORE_CTX_idx() < 0) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS); + goto err; + } + + ret = (SSL_CTX *)OPENSSL_malloc(sizeof(SSL_CTX)); + if (ret == NULL) { + goto err; + } + + memset(ret, 0, sizeof(SSL_CTX)); + + ret->method = meth->method; + + ret->cert_store = NULL; + ret->session_cache_mode = SSL_SESS_CACHE_SERVER; + ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT; + ret->session_cache_head = NULL; + ret->session_cache_tail = NULL; + + /* We take the system default */ + ret->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT; + + ret->new_session_cb = 0; + ret->remove_session_cb = 0; + ret->get_session_cb = 0; + ret->generate_session_id = 0; + + memset((char *)&ret->stats, 0, sizeof(ret->stats)); + + ret->references = 1; + ret->quiet_shutdown = 0; + + ret->info_callback = NULL; + + ret->app_verify_callback = 0; + ret->app_verify_arg = NULL; + + ret->max_cert_list = SSL_MAX_CERT_LIST_DEFAULT; + ret->read_ahead = 0; + ret->msg_callback = 0; + ret->msg_callback_arg = NULL; + ret->verify_mode = SSL_VERIFY_NONE; + ret->sid_ctx_length = 0; + ret->default_verify_callback = NULL; + ret->cert = ssl_cert_new(); + if (ret->cert == NULL) { + goto err; + } + + ret->default_passwd_callback = 0; + ret->default_passwd_callback_userdata = NULL; + ret->client_cert_cb = 0; + ret->app_gen_cookie_cb = 0; + ret->app_verify_cookie_cb = 0; + + ret->sessions = lh_SSL_SESSION_new(ssl_session_hash, ssl_session_cmp); + if (ret->sessions == NULL) { + goto err; + } + ret->cert_store = X509_STORE_new(); + if (ret->cert_store == NULL) { + goto err; + } + + ssl_create_cipher_list(ret->method, &ret->cipher_list, + &ret->cipher_list_by_id, SSL_DEFAULT_CIPHER_LIST, + ret->cert); + if (ret->cipher_list == NULL || + sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, SSL_R_LIBRARY_HAS_NO_CIPHERS); + goto err2; + } + + ret->param = X509_VERIFY_PARAM_new(); + if (!ret->param) { + goto err; + } + + ret->client_CA = sk_X509_NAME_new_null(); + if (ret->client_CA == NULL) { + goto err; + } + + CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_CTX, ret, &ret->ex_data); + + ret->extra_certs = NULL; + + ret->max_send_fragment = SSL3_RT_MAX_PLAIN_LENGTH; + + ret->tlsext_servername_callback = 0; + ret->tlsext_servername_arg = NULL; + /* 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; + } + + ret->tlsext_status_cb = 0; + ret->tlsext_status_arg = NULL; + + ret->next_protos_advertised_cb = 0; + ret->next_proto_select_cb = 0; + ret->psk_identity_hint = NULL; + ret->psk_client_callback = NULL; + ret->psk_server_callback = NULL; + + /* Default is to connect to non-RI servers. When RI is more widely deployed + * might change this. */ + ret->options |= SSL_OP_LEGACY_SERVER_CONNECT; + + /* Lock the SSL_CTX to the specified version, for compatibility with legacy + * uses of SSL_METHOD. */ + if (meth->version != 0) { + SSL_CTX_set_max_version(ret, meth->version); + SSL_CTX_set_min_version(ret, meth->version); + } + + return ret; + +err: + OPENSSL_PUT_ERROR(SSL, SSL_CTX_new, ERR_R_MALLOC_FAILURE); +err2: + if (ret != NULL) { + SSL_CTX_free(ret); + } + return NULL; +} + +void SSL_CTX_free(SSL_CTX *a) { + int i; + + if (a == NULL) { + return; + } + + i = CRYPTO_add(&a->references, -1, CRYPTO_LOCK_SSL_CTX); + if (i > 0) { + return; + } + + if (a->param) { + X509_VERIFY_PARAM_free(a->param); + } + + /* 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].) */ + if (a->sessions != NULL) { + SSL_CTX_flush_sessions(a, 0); + } + + CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_CTX, a, &a->ex_data); + + if (a->sessions != NULL) { + lh_SSL_SESSION_free(a->sessions); + } + if (a->cert_store != NULL) { + X509_STORE_free(a->cert_store); + } + if (a->cipher_list != NULL) { + ssl_cipher_preference_list_free(a->cipher_list); + } + if (a->cipher_list_by_id != NULL) { + sk_SSL_CIPHER_free(a->cipher_list_by_id); + } + if (a->cipher_list_tls11 != NULL) { + ssl_cipher_preference_list_free(a->cipher_list_tls11); + } + if (a->cert != NULL) { + ssl_cert_free(a->cert); + } + if (a->client_CA != NULL) { + sk_X509_NAME_pop_free(a->client_CA, X509_NAME_free); + } + if (a->extra_certs != NULL) { + sk_X509_pop_free(a->extra_certs, X509_free); + } + if (a->srtp_profiles) { + sk_SRTP_PROTECTION_PROFILE_free(a->srtp_profiles); + } + if (a->psk_identity_hint) { + OPENSSL_free(a->psk_identity_hint); + } + if (a->tlsext_ecpointformatlist) { + OPENSSL_free(a->tlsext_ecpointformatlist); + } + if (a->tlsext_ellipticcurvelist) { + OPENSSL_free(a->tlsext_ellipticcurvelist); + } + if (a->alpn_client_proto_list != NULL) { + OPENSSL_free(a->alpn_client_proto_list); + } + if (a->tlsext_channel_id_private) { + EVP_PKEY_free(a->tlsext_channel_id_private); + } + if (a->keylog_bio) { + BIO_free(a->keylog_bio); + } + + OPENSSL_free(a); +} + +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 *u) { + ctx->default_passwd_callback_userdata = u; +} + +void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, + int (*cb)(X509_STORE_CTX *, void *), + void *arg) { + 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 *)) { + ctx->verify_mode = mode; + ctx->default_verify_callback = cb; +} + +void SSL_CTX_set_verify_depth(SSL_CTX *ctx, int depth) { + X509_VERIFY_PARAM_set_depth(ctx->param, depth); +} + +void SSL_CTX_set_cert_cb(SSL_CTX *c, int (*cb)(SSL *ssl, void *arg), + void *arg) { + ssl_cert_set_cert_cb(c->cert, cb, arg); +} + +void SSL_set_cert_cb(SSL *s, int (*cb)(SSL *ssl, void *arg), void *arg) { + ssl_cert_set_cert_cb(s->cert, cb, arg); +} + +static int ssl_has_key(SSL *s, size_t idx) { + CERT_PKEY *cpk = &s->cert->pkeys[idx]; + return cpk->x509 && cpk->privatekey; +} + +void ssl_get_compatible_server_ciphers(SSL *s, unsigned long *out_mask_k, + unsigned long *out_mask_a) { + CERT *c = s->cert; + int rsa_enc, rsa_sign, dh_tmp; + unsigned long mask_k, mask_a; + int have_ecc_cert, ecdsa_ok; + int have_ecdh_tmp; + X509 *x; + + if (c == NULL) { + /* TODO(davidben): Is this codepath possible? */ + *out_mask_k = 0; + *out_mask_a = 0; + return; + } + + dh_tmp = (c->dh_tmp != NULL || c->dh_tmp_cb != NULL); + + have_ecdh_tmp = (c->ecdh_tmp || c->ecdh_tmp_cb || c->ecdh_tmp_auto); + rsa_enc = ssl_has_key(s, SSL_PKEY_RSA_ENC); + rsa_sign = ssl_has_key(s, SSL_PKEY_RSA_SIGN); + have_ecc_cert = ssl_has_key(s, SSL_PKEY_ECC); + mask_k = 0; + mask_a = 0; + + if (rsa_enc) { + mask_k |= SSL_kRSA; + } + if (dh_tmp) { + mask_k |= SSL_kEDH; + } + if (rsa_enc || rsa_sign) { + mask_a |= SSL_aRSA; + } + + mask_a |= SSL_aNULL; + + /* An ECC certificate may be usable for ECDSA cipher suites depending on the + * key usage extension and on the client's curve preferences. */ + if (have_ecc_cert) { + x = c->pkeys[SSL_PKEY_ECC].x509; + /* This call populates extension flags (ex_flags). */ + X509_check_purpose(x, -1, 0); + ecdsa_ok = (x->ex_flags & EXFLAG_KUSAGE) + ? (x->ex_kusage & X509v3_KU_DIGITAL_SIGNATURE) + : 1; + if (!tls1_check_ec_cert(s, x)) { + ecdsa_ok = 0; + } + if (ecdsa_ok) { + mask_a |= SSL_aECDSA; + } + } + + /* If we are considering an ECC cipher suite that uses an ephemeral EC + * key, check it. */ + if (have_ecdh_tmp && tls1_check_ec_tmp_key(s)) { + mask_k |= SSL_kEECDH; + } + + /* PSK requires a server callback. */ + if (s->psk_server_callback != NULL) { + mask_k |= SSL_kPSK; + mask_a |= SSL_aPSK; + } + + *out_mask_k = mask_k; + *out_mask_a = mask_a; +} + +/* This handy macro borrowed from crypto/x509v3/v3_purp.c */ +#define ku_reject(x, usage) \ + (((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage))) + +int ssl_check_srvr_ecc_cert_and_alg(X509 *x, SSL *s) { + unsigned long alg_a; + int signature_nid = 0, md_nid = 0, pk_nid = 0; + const SSL_CIPHER *cs = s->s3->tmp.new_cipher; + + alg_a = cs->algorithm_auth; + + /* This call populates the ex_flags field correctly */ + X509_check_purpose(x, -1, 0); + if (x->sig_alg && x->sig_alg->algorithm) { + signature_nid = OBJ_obj2nid(x->sig_alg->algorithm); + OBJ_find_sigid_algs(signature_nid, &md_nid, &pk_nid); + } + if (alg_a & SSL_aECDSA) { + /* key usage, if present, must allow signing */ + if (ku_reject(x, X509v3_KU_DIGITAL_SIGNATURE)) { + OPENSSL_PUT_ERROR(SSL, ssl_check_srvr_ecc_cert_and_alg, + SSL_R_ECC_CERT_NOT_FOR_SIGNING); + return 0; + } + } + + return 1; /* all checks are ok */ +} + +static int ssl_get_server_cert_index(const SSL *s) { + int idx; + idx = ssl_cipher_get_cert_index(s->s3->tmp.new_cipher); + if (idx == SSL_PKEY_RSA_ENC && !s->cert->pkeys[SSL_PKEY_RSA_ENC].x509) { + idx = SSL_PKEY_RSA_SIGN; + } + if (idx == -1) { + OPENSSL_PUT_ERROR(SSL, ssl_get_server_cert_index, ERR_R_INTERNAL_ERROR); + } + return idx; +} + +CERT_PKEY *ssl_get_server_send_pkey(const SSL *s) { + int i = ssl_get_server_cert_index(s); + + /* This may or may not be an error. */ + if (i < 0) { + return NULL; + } + + /* May be NULL. */ + return &s->cert->pkeys[i]; +} + +EVP_PKEY *ssl_get_sign_pkey(SSL *s, const SSL_CIPHER *cipher) { + unsigned long alg_a; + CERT *c; + int idx = -1; + + alg_a = cipher->algorithm_auth; + c = s->cert; + + if (alg_a & SSL_aRSA) { + if (c->pkeys[SSL_PKEY_RSA_SIGN].privatekey != NULL) { + idx = SSL_PKEY_RSA_SIGN; + } else if (c->pkeys[SSL_PKEY_RSA_ENC].privatekey != NULL) { + idx = SSL_PKEY_RSA_ENC; + } + } else if ((alg_a & SSL_aECDSA) && + (c->pkeys[SSL_PKEY_ECC].privatekey != NULL)) { + idx = SSL_PKEY_ECC; + } + + if (idx == -1) { + OPENSSL_PUT_ERROR(SSL, ssl_get_sign_pkey, ERR_R_INTERNAL_ERROR); + return NULL; + } + + return c->pkeys[idx].privatekey; +} + +void ssl_update_cache(SSL *s, int mode) { + int i; + + /* If the session_id_length is 0, we are not supposed to cache it, and it + * would be rather hard to do anyway :-) */ + if (s->session->session_id_length == 0) { + return; + } + + i = s->initial_ctx->session_cache_mode; + if ((i & mode) && !s->hit && + ((i & SSL_SESS_CACHE_NO_INTERNAL_STORE) || + SSL_CTX_add_session(s->initial_ctx, s->session)) && + s->initial_ctx->new_session_cb != NULL) { + CRYPTO_add(&s->session->references, 1, CRYPTO_LOCK_SSL_SESSION); + if (!s->initial_ctx->new_session_cb(s, s->session)) { + SSL_SESSION_free(s->session); + } + } + + /* auto flush every 255 connections */ + if ((!(i & SSL_SESS_CACHE_NO_AUTO_CLEAR)) && ((i & mode) == mode)) { + if ((((mode & SSL_SESS_CACHE_CLIENT) + ? s->initial_ctx->stats.sess_connect_good + : s->initial_ctx->stats.sess_accept_good) & + 0xff) == 0xff) { + SSL_CTX_flush_sessions(s->initial_ctx, (unsigned long)time(NULL)); + } + } +} + +int SSL_get_error(const SSL *s, int i) { + int reason; + unsigned long l; + BIO *bio; + + if (i > 0) { + return SSL_ERROR_NONE; + } + + /* Make things return SSL_ERROR_SYSCALL when doing SSL_do_handshake etc, + * where we do encode the error */ + l = ERR_peek_error(); + if (l != 0) { + if (ERR_GET_LIB(l) == ERR_LIB_SYS) { + return SSL_ERROR_SYSCALL; + } + return SSL_ERROR_SSL; + } + + if (i == 0) { + if ((s->shutdown & SSL_RECEIVED_SHUTDOWN) && + (s->s3->warn_alert == SSL_AD_CLOSE_NOTIFY)) { + /* The socket was cleanly shut down with a 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; + } + + if (SSL_want_session(s)) { + return SSL_ERROR_PENDING_SESSION; + } + + if (SSL_want_certificate(s)) { + return SSL_ERROR_PENDING_CERTIFICATE; + } + + if (SSL_want_read(s)) { + bio = SSL_get_rbio(s); + if (BIO_should_read(bio)) { + return SSL_ERROR_WANT_READ; + } + + if (BIO_should_write(bio)) { + /* This one doesn't make too much sense ... We never try to write to the + * rbio, and an application program where rbio and wbio are separate + * couldn't even know what it should wait for. However if we ever set + * s->rwstate incorrectly (so that we have SSL_want_read(s) instead of + * SSL_want_write(s)) and rbio and wbio *are* the same, this test works + * around that bug; so it might be safer to keep it. */ + return SSL_ERROR_WANT_WRITE; + } + + if (BIO_should_io_special(bio)) { + reason = BIO_get_retry_reason(bio); + if (reason == BIO_RR_CONNECT) { + return SSL_ERROR_WANT_CONNECT; + } + + if (reason == BIO_RR_ACCEPT) { + return SSL_ERROR_WANT_ACCEPT; + } + + return SSL_ERROR_SYSCALL; /* unknown */ + } + } + + if (SSL_want_write(s)) { + bio = SSL_get_wbio(s); + if (BIO_should_write(bio)) { + return SSL_ERROR_WANT_WRITE; + } + + if (BIO_should_read(bio)) { + /* See above (SSL_want_read(s) with BIO_should_write(bio)) */ + return SSL_ERROR_WANT_READ; + } + + if (BIO_should_io_special(bio)) { + reason = BIO_get_retry_reason(bio); + if (reason == BIO_RR_CONNECT) { + return SSL_ERROR_WANT_CONNECT; + } + + if (reason == BIO_RR_ACCEPT) { + return SSL_ERROR_WANT_ACCEPT; + } + + return SSL_ERROR_SYSCALL; + } + } + + if (SSL_want_x509_lookup(s)) { + return SSL_ERROR_WANT_X509_LOOKUP; + } + + if (SSL_want_channel_id_lookup(s)) { + return SSL_ERROR_WANT_CHANNEL_ID_LOOKUP; + } + + return SSL_ERROR_SYSCALL; +} + +int SSL_do_handshake(SSL *s) { + int ret = 1; + + if (s->handshake_func == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_do_handshake, SSL_R_CONNECTION_TYPE_NOT_SET); + return -1; + } + + s->method->ssl_renegotiate_check(s); + + if (SSL_in_init(s)) { + ret = s->handshake_func(s); + } + return ret; +} + +void SSL_set_accept_state(SSL *s) { + s->server = 1; + s->shutdown = 0; + s->state = SSL_ST_ACCEPT | SSL_ST_BEFORE; + s->handshake_func = s->method->ssl_accept; + /* clear the current cipher */ + ssl_clear_cipher_ctx(s); +} + +void SSL_set_connect_state(SSL *s) { + s->server = 0; + s->shutdown = 0; + s->state = SSL_ST_CONNECT | SSL_ST_BEFORE; + s->handshake_func = s->method->ssl_connect; + /* clear the current cipher */ + ssl_clear_cipher_ctx(s); +} + +int ssl_undefined_function(SSL *s) { + OPENSSL_PUT_ERROR(SSL, ssl_undefined_function, + ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; +} + +int ssl_undefined_void_function(void) { + OPENSSL_PUT_ERROR(SSL, ssl_undefined_void_function, + ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; +} + +int ssl_undefined_const_function(const SSL *s) { + OPENSSL_PUT_ERROR(SSL, ssl_undefined_const_function, + ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; +} + +static const char *ssl_get_version(int version) { + switch (version) { + case TLS1_2_VERSION: + return "TLSv1.2"; + + case TLS1_1_VERSION: + return "TLSv1.1"; + + case TLS1_VERSION: + return "TLSv1"; + + case SSL3_VERSION: + return "SSLv3"; + + default: + return "unknown"; + } +} + +const char *SSL_get_version(const SSL *s) { + return ssl_get_version(s->version); +} + +const char *SSL_SESSION_get_version(const SSL_SESSION *sess) { + return ssl_get_version(sess->ssl_version); +} + +void ssl_clear_cipher_ctx(SSL *s) { + if (s->aead_read_ctx != NULL) { + EVP_AEAD_CTX_cleanup(&s->aead_read_ctx->ctx); + OPENSSL_free(s->aead_read_ctx); + s->aead_read_ctx = NULL; + } + + if (s->aead_write_ctx != NULL) { + EVP_AEAD_CTX_cleanup(&s->aead_write_ctx->ctx); + OPENSSL_free(s->aead_write_ctx); + s->aead_write_ctx = NULL; + } +} + +X509 *SSL_get_certificate(const SSL *s) { + if (s->cert != NULL) { + return s->cert->key->x509; + } + + return NULL; +} + +EVP_PKEY *SSL_get_privatekey(const SSL *s) { + if (s->cert != NULL) { + return s->cert->key->privatekey; + } + + return NULL; +} + +X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) { + if (ctx->cert != NULL) { + return ctx->cert->key->x509; + } + + return NULL; +} + +EVP_PKEY *SSL_CTX_get0_privatekey(const SSL_CTX *ctx) { + if (ctx->cert != NULL) { + return ctx->cert->key->privatekey; + } + + return NULL; +} + +const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) { + if (s->session != NULL && s->session->cipher != NULL) { + return s->session->cipher; + } + + return NULL; +} + +const void *SSL_get_current_compression(SSL *s) { return NULL; } + +const void *SSL_get_current_expansion(SSL *s) { return NULL; } + +int ssl_init_wbio_buffer(SSL *s, int push) { + BIO *bbio; + + if (s->bbio == NULL) { + bbio = BIO_new(BIO_f_buffer()); + if (bbio == NULL) { + return 0; + } + s->bbio = bbio; + } else { + bbio = s->bbio; + if (s->bbio == s->wbio) { + s->wbio = BIO_pop(s->wbio); + } + } + + BIO_reset(bbio); + if (!BIO_set_read_buffer_size(bbio, 1)) { + OPENSSL_PUT_ERROR(SSL, ssl_init_wbio_buffer, ERR_R_BUF_LIB); + return 0; + } + + if (push) { + if (s->wbio != bbio) { + s->wbio = BIO_push(bbio, s->wbio); + } + } else { + if (s->wbio == bbio) { + s->wbio = BIO_pop(bbio); + } + } + + return 1; +} + +void ssl_free_wbio_buffer(SSL *s) { + if (s->bbio == NULL) { + return; + } + + if (s->bbio == s->wbio) { + /* remove buffering */ + s->wbio = BIO_pop(s->wbio); + } + + BIO_free(s->bbio); + s->bbio = NULL; +} + +void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) { + ctx->quiet_shutdown = mode; +} + +int SSL_CTX_get_quiet_shutdown(const SSL_CTX *ctx) { + return ctx->quiet_shutdown; +} + +void SSL_set_quiet_shutdown(SSL *s, int mode) { s->quiet_shutdown = mode; } + +int SSL_get_quiet_shutdown(const SSL *s) { return s->quiet_shutdown; } + +void SSL_set_shutdown(SSL *s, int mode) { s->shutdown = mode; } + +int SSL_get_shutdown(const SSL *s) { return s->shutdown; } + +int SSL_version(const SSL *s) { return s->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; + } + + if (ctx == NULL) { + ctx = ssl->initial_ctx; + } + + if (ssl->cert != NULL) { + ssl_cert_free(ssl->cert); + } + + ssl->cert = ssl_cert_dup(ctx->cert); + CRYPTO_add(&ctx->references, 1, CRYPTO_LOCK_SSL_CTX); + if (ssl->ctx != NULL) { + SSL_CTX_free(ssl->ctx); /* decrement reference count */ + } + ssl->ctx = ctx; + + ssl->sid_ctx_length = ctx->sid_ctx_length; + assert(ssl->sid_ctx_length <= sizeof(ssl->sid_ctx)); + memcpy(ssl->sid_ctx, ctx->sid_ctx, sizeof(ssl->sid_ctx)); + + return ssl->ctx; +} + +int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) { + return X509_STORE_set_default_paths(ctx->cert_store); +} + +int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, + const char *CApath) { + return X509_STORE_load_locations(ctx->cert_store, CAfile, CApath); +} + +void SSL_set_info_callback(SSL *ssl, + void (*cb)(const SSL *ssl, int type, int val)) { + ssl->info_callback = cb; +} + +void (*SSL_get_info_callback(const SSL *ssl))(const SSL * /*ssl*/, int /*type*/, + int /*val*/) { + return ssl->info_callback; +} + +int SSL_state(const SSL *ssl) { return ssl->state; } + +void SSL_set_state(SSL *ssl, int state) { ssl->state = state; } + +void SSL_set_verify_result(SSL *ssl, long arg) { ssl->verify_result = arg; } + +long SSL_get_verify_result(const SSL *ssl) { return ssl->verify_result; } + +int SSL_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func) { + return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, argl, argp, new_func, + dup_func, free_func); +} + +int SSL_set_ex_data(SSL *s, int idx, void *arg) { + return CRYPTO_set_ex_data(&s->ex_data, idx, arg); +} + +void *SSL_get_ex_data(const SSL *s, int idx) { + return CRYPTO_get_ex_data(&s->ex_data, idx); +} + +int SSL_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, + CRYPTO_EX_free *free_func) { + return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_CTX, argl, argp, new_func, + dup_func, free_func); +} + +int SSL_CTX_set_ex_data(SSL_CTX *s, int idx, void *arg) { + return CRYPTO_set_ex_data(&s->ex_data, idx, arg); +} + +void *SSL_CTX_get_ex_data(const SSL_CTX *s, int idx) { + return CRYPTO_get_ex_data(&s->ex_data, idx); +} + +int ssl_ok(SSL *s) { return 1; } + +X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *ctx) { + return ctx->cert_store; +} + +void SSL_CTX_set_cert_store(SSL_CTX *ctx, X509_STORE *store) { + if (ctx->cert_store != NULL) { + X509_STORE_free(ctx->cert_store); + } + ctx->cert_store = store; +} + +int SSL_want(const SSL *s) { return s->rwstate; } + +void SSL_CTX_set_tmp_rsa_callback(SSL_CTX *ctx, + RSA *(*cb)(SSL *ssl, int is_export, + int keylength)) { + SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TMP_RSA_CB, (void (*)(void))cb); +} + +void SSL_set_tmp_rsa_callback(SSL *ssl, RSA *(*cb)(SSL *ssl, int is_export, + int keylength)) { + SSL_callback_ctrl(ssl, SSL_CTRL_SET_TMP_RSA_CB, (void (*)(void))cb); +} + +void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, + DH *(*dh)(SSL *ssl, int is_export, + int keylength)) { + SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TMP_DH_CB, (void (*)(void))dh); +} + +void SSL_set_tmp_dh_callback(SSL *ssl, DH *(*dh)(SSL *ssl, int is_export, + int keylength)) { + SSL_callback_ctrl(ssl, SSL_CTRL_SET_TMP_DH_CB, (void (*)(void))dh); +} + +void SSL_CTX_set_tmp_ecdh_callback(SSL_CTX *ctx, + EC_KEY *(*ecdh)(SSL *ssl, int is_export, + int keylength)) { + SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_TMP_ECDH_CB, (void (*)(void))ecdh); +} + +void SSL_set_tmp_ecdh_callback(SSL *ssl, + EC_KEY *(*ecdh)(SSL *ssl, int is_export, + int keylength)) { + SSL_callback_ctrl(ssl, SSL_CTRL_SET_TMP_ECDH_CB, (void (*)(void))ecdh); +} + +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_CTX_use_psk_identity_hint, + SSL_R_DATA_LENGTH_TOO_LONG); + return 0; + } + + if (ctx->psk_identity_hint != NULL) { + 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 *s, const char *identity_hint) { + if (s == NULL) { + return 0; + } + + if (identity_hint != NULL && strlen(identity_hint) > PSK_MAX_IDENTITY_LEN) { + OPENSSL_PUT_ERROR(SSL, SSL_use_psk_identity_hint, + SSL_R_DATA_LENGTH_TOO_LONG); + return 0; + } + + /* Clear currently configured hint, if any. */ + if (s->psk_identity_hint != NULL) { + OPENSSL_free(s->psk_identity_hint); + s->psk_identity_hint = NULL; + } + + if (identity_hint != NULL) { + s->psk_identity_hint = BUF_strdup(identity_hint); + if (s->psk_identity_hint == NULL) { + return 0; + } + } + + return 1; +} + +const char *SSL_get_psk_identity_hint(const SSL *s) { + if (s == NULL) { + return NULL; + } + return s->psk_identity_hint; +} + +const char *SSL_get_psk_identity(const SSL *s) { + if (s == NULL || s->session == NULL) { + return NULL; + } + + return s->session->psk_identity; +} + +void SSL_set_psk_client_callback( + SSL *s, unsigned int (*cb)(SSL *ssl, const char *hint, char *identity, + unsigned int max_identity_len, uint8_t *psk, + unsigned int max_psk_len)) { + s->psk_client_callback = cb; +} + +void SSL_CTX_set_psk_client_callback( + SSL_CTX *ctx, unsigned int (*cb)(SSL *ssl, const char *hint, char *identity, + unsigned int max_identity_len, + uint8_t *psk, unsigned int max_psk_len)) { + ctx->psk_client_callback = cb; +} + +void SSL_set_psk_server_callback( + SSL *s, unsigned int (*cb)(SSL *ssl, const char *identity, uint8_t *psk, + unsigned int max_psk_len)) { + s->psk_server_callback = cb; +} + +void SSL_CTX_set_psk_server_callback( + SSL_CTX *ctx, unsigned int (*cb)(SSL *ssl, const char *identity, + uint8_t *psk, unsigned int max_psk_len)) { + ctx->psk_server_callback = cb; +} + +void SSL_CTX_set_min_version(SSL_CTX *ctx, uint16_t version) { + ctx->min_version = version; +} + +void SSL_CTX_set_max_version(SSL_CTX *ctx, uint16_t version) { + ctx->max_version = version; +} + +void SSL_set_min_version(SSL *ssl, uint16_t version) { + ssl->min_version = version; +} + +void SSL_set_max_version(SSL *ssl, uint16_t version) { + ssl->max_version = version; +} + +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)) { + SSL_CTX_callback_ctrl(ctx, SSL_CTRL_SET_MSG_CALLBACK, (void (*)(void))cb); +} +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_callback_ctrl(ssl, SSL_CTRL_SET_MSG_CALLBACK, (void (*)(void))cb); +} + +void SSL_CTX_set_keylog_bio(SSL_CTX *ctx, BIO *keylog_bio) { + if (ctx->keylog_bio != NULL) { + BIO_free(ctx->keylog_bio); + } + ctx->keylog_bio = keylog_bio; +} + +static int cbb_add_hex(CBB *cbb, const uint8_t *in, size_t in_len) { + static const char hextable[] = "0123456789abcdef"; + uint8_t *out; + size_t i; + + if (!CBB_add_space(cbb, &out, in_len * 2)) { + return 0; + } + + for (i = 0; i < in_len; i++) { + *(out++) = (uint8_t)hextable[in[i] >> 4]; + *(out++) = (uint8_t)hextable[in[i] & 0xf]; + } + + return 1; +} + +int ssl_ctx_log_rsa_client_key_exchange(SSL_CTX *ctx, + const uint8_t *encrypted_premaster, + size_t encrypted_premaster_len, + const uint8_t *premaster, + size_t premaster_len) { + BIO *bio = ctx->keylog_bio; + CBB cbb; + uint8_t *out; + size_t out_len; + int ret; + + if (bio == NULL) { + return 1; + } + + if (encrypted_premaster_len < 8) { + OPENSSL_PUT_ERROR(SSL, ssl_ctx_log_rsa_client_key_exchange, + ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!CBB_init(&cbb, 4 + 16 + 1 + premaster_len * 2 + 1)) { + return 0; + } + + if (!CBB_add_bytes(&cbb, (const uint8_t *)"RSA ", 4) || + /* Only the first 8 bytes of the encrypted premaster secret are + * logged. */ + !cbb_add_hex(&cbb, encrypted_premaster, 8) || + !CBB_add_bytes(&cbb, (const uint8_t *)" ", 1) || + !cbb_add_hex(&cbb, premaster, premaster_len) || + !CBB_add_bytes(&cbb, (const uint8_t *)"\n", 1) || + !CBB_finish(&cbb, &out, &out_len)) { + CBB_cleanup(&cbb); + return 0; + } + + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + ret = BIO_write(bio, out, out_len) >= 0 && BIO_flush(bio); + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + + OPENSSL_free(out); + return ret; +} + +int ssl_ctx_log_master_secret(SSL_CTX *ctx, const uint8_t *client_random, + size_t client_random_len, const uint8_t *master, + size_t master_len) { + BIO *bio = ctx->keylog_bio; + CBB cbb; + uint8_t *out; + size_t out_len; + int ret; + + if (bio == NULL) { + return 1; + } + + if (client_random_len != 32) { + OPENSSL_PUT_ERROR(SSL, ssl_ctx_log_master_secret, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (!CBB_init(&cbb, 14 + 64 + 1 + master_len * 2 + 1)) { + return 0; + } + + if (!CBB_add_bytes(&cbb, (const uint8_t *)"CLIENT_RANDOM ", 14) || + !cbb_add_hex(&cbb, client_random, 32) || + !CBB_add_bytes(&cbb, (const uint8_t *)" ", 1) || + !cbb_add_hex(&cbb, master, master_len) || + !CBB_add_bytes(&cbb, (const uint8_t *)"\n", 1) || + !CBB_finish(&cbb, &out, &out_len)) { + CBB_cleanup(&cbb); + return 0; + } + + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + ret = BIO_write(bio, out, out_len) >= 0 && BIO_flush(bio); + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + + OPENSSL_free(out); + return ret; +} + +int SSL_cutthrough_complete(const SSL *s) { + return ( + !s->server && /* cutthrough only applies to clients */ + !s->hit && /* full-handshake */ + s->version >= SSL3_VERSION && + s->s3->in_read_app_data == 0 && /* cutthrough only applies to write() */ + (SSL_get_mode((SSL *)s) & + SSL_MODE_HANDSHAKE_CUTTHROUGH) && /* cutthrough enabled */ + ssl3_can_cutthrough(s) && /* cutthrough allowed */ + s->s3->previous_server_finished_len == + 0 && /* not a renegotiation handshake */ + (s->state == SSL3_ST_CR_SESSION_TICKET_A || /* ready to write app-data*/ + s->state == SSL3_ST_CR_CHANGE || s->state == SSL3_ST_CR_FINISHED_A)); +} + +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_cutthrough(const SSL *s) { + const SSL_CIPHER *c; + + /* require a strong enough cipher */ + if (SSL_get_cipher_bits(s, NULL) < 128) { + return 0; + } + + /* require ALPN or NPN extension */ + if (!s->s3->alpn_selected && !s->s3->next_proto_neg_seen) { + return 0; + } + + /* require a forward-secret cipher */ + c = SSL_get_current_cipher(s); + if (!c || + (c->algorithm_mkey != SSL_kEDH && c->algorithm_mkey != SSL_kEECDH)) { + return 0; + } + + return 1; +} + +const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version) { + switch (version) { + case SSL3_VERSION: + return &SSLv3_enc_data; + + case TLS1_VERSION: + return &TLSv1_enc_data; + + case TLS1_1_VERSION: + return &TLSv1_1_enc_data; + + case TLS1_2_VERSION: + return &TLSv1_2_enc_data; + + case DTLS1_VERSION: + return &DTLSv1_enc_data; + + case DTLS1_2_VERSION: + return &DTLSv1_2_enc_data; + + default: + return NULL; + } +} + +uint16_t ssl3_get_max_server_version(const SSL *s) { + uint16_t max_version; + + if (SSL_IS_DTLS(s)) { + max_version = (s->max_version != 0) ? s->max_version : DTLS1_2_VERSION; + if (!(s->options & SSL_OP_NO_DTLSv1_2) && DTLS1_2_VERSION >= max_version) { + return DTLS1_2_VERSION; + } + if (!(s->options & SSL_OP_NO_DTLSv1) && DTLS1_VERSION >= max_version) { + return DTLS1_VERSION; + } + return 0; + } + + max_version = (s->max_version != 0) ? s->max_version : TLS1_2_VERSION; + if (!(s->options & SSL_OP_NO_TLSv1_2) && TLS1_2_VERSION <= max_version) { + return TLS1_2_VERSION; + } + if (!(s->options & SSL_OP_NO_TLSv1_1) && TLS1_1_VERSION <= max_version) { + return TLS1_1_VERSION; + } + if (!(s->options & SSL_OP_NO_TLSv1) && TLS1_VERSION <= max_version) { + return TLS1_VERSION; + } + if (!(s->options & SSL_OP_NO_SSLv3) && SSL3_VERSION <= max_version) { + return SSL3_VERSION; + } + return 0; +} + +uint16_t ssl3_get_mutual_version(SSL *s, uint16_t client_version) { + uint16_t version = 0; + + if (SSL_IS_DTLS(s)) { + /* Clamp client_version to max_version. */ + if (s->max_version != 0 && client_version < s->max_version) { + client_version = s->max_version; + } + + if (client_version <= DTLS1_2_VERSION && !(s->options & SSL_OP_NO_DTLSv1_2)) { + version = DTLS1_2_VERSION; + } else if (client_version <= DTLS1_VERSION && + !(s->options & SSL_OP_NO_DTLSv1)) { + version = DTLS1_VERSION; + } + + /* Check against min_version. */ + if (version != 0 && s->min_version != 0 && version > s->min_version) { + return 0; + } + return version; + } else { + /* Clamp client_version to max_version. */ + if (s->max_version != 0 && client_version > s->max_version) { + client_version = s->max_version; + } + + if (client_version >= TLS1_2_VERSION && !(s->options & SSL_OP_NO_TLSv1_2)) { + version = TLS1_2_VERSION; + } else if (client_version >= TLS1_1_VERSION && + !(s->options & SSL_OP_NO_TLSv1_1)) { + version = TLS1_1_VERSION; + } else if (client_version >= TLS1_VERSION && !(s->options & SSL_OP_NO_TLSv1)) { + version = TLS1_VERSION; + } else if (client_version >= SSL3_VERSION && !(s->options & SSL_OP_NO_SSLv3)) { + version = SSL3_VERSION; + } + + /* Check against min_version. */ + if (version != 0 && s->min_version != 0 && version < s->min_version) { + return 0; + } + return version; + } +} + +uint16_t ssl3_get_max_client_version(SSL *s) { + unsigned long options = s->options; + uint16_t version = 0; + + /* 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 TLS 1.2 in a future-proof way. + * + * By this scheme, the maximum version is the lowest version V such that V is + * enabled and V+1 is disabled or unimplemented. */ + if (SSL_IS_DTLS(s)) { + if (!(options & SSL_OP_NO_DTLSv1_2)) { + version = DTLS1_2_VERSION; + } + if (!(options & SSL_OP_NO_DTLSv1) && (options & SSL_OP_NO_DTLSv1_2)) { + version = DTLS1_VERSION; + } + if (s->max_version != 0 && version < s->max_version) { + version = s->max_version; + } + } else { + if (!(options & SSL_OP_NO_TLSv1_2)) { + version = TLS1_2_VERSION; + } + if (!(options & SSL_OP_NO_TLSv1_1) && (options & SSL_OP_NO_TLSv1_2)) { + version = TLS1_1_VERSION; + } + if (!(options & SSL_OP_NO_TLSv1) && (options & SSL_OP_NO_TLSv1_1)) { + version = TLS1_VERSION; + } + if (!(options & SSL_OP_NO_SSLv3) && (options & SSL_OP_NO_TLSv1)) { + version = SSL3_VERSION; + } + if (s->max_version != 0 && version > s->max_version) { + version = s->max_version; + } + } + + return version; +} + +int ssl3_is_version_enabled(SSL *s, uint16_t version) { + if (SSL_IS_DTLS(s)) { + if (s->max_version != 0 && version < s->max_version) { + return 0; + } + if (s->min_version != 0 && version > s->min_version) { + return 0; + } + + switch (version) { + case DTLS1_VERSION: + return !(s->options & SSL_OP_NO_DTLSv1); + + case DTLS1_2_VERSION: + return !(s->options & SSL_OP_NO_DTLSv1_2); + + default: + return 0; + } + } else { + if (s->max_version != 0 && version > s->max_version) { + return 0; + } + if (s->min_version != 0 && version < s->min_version) { + return 0; + } + + switch (version) { + case SSL3_VERSION: + return !(s->options & SSL_OP_NO_SSLv3); + + case TLS1_VERSION: + return !(s->options & SSL_OP_NO_TLSv1); + + case TLS1_1_VERSION: + return !(s->options & SSL_OP_NO_TLSv1_1); + + case TLS1_2_VERSION: + return !(s->options & SSL_OP_NO_TLSv1_2); + + default: + return 0; + } + } +} + +uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version) { + if (!SSL_IS_DTLS(s)) { + return wire_version; + } + + uint16_t tls_version = ~wire_version; + uint16_t version = tls_version + 0x0201; + /* If either component overflowed, clamp it so comparisons still work. */ + if ((version >> 8) < (tls_version >> 8)) { + version = 0xff00 | (version & 0xff); + } + if ((version & 0xff) < (tls_version & 0xff)) { + version = (version & 0xff00) | 0xff; + } + /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */ + if (version == TLS1_VERSION) { + version = TLS1_1_VERSION; + } + return version; +} + +int SSL_cache_hit(SSL *s) { return s->hit; } + +int SSL_is_server(SSL *s) { return s->server; } + +void SSL_enable_fastradio_padding(SSL *s, char on_off) { + s->fastradio_padding = on_off; +} diff --git a/src/ssl/ssl_locl.h b/src/ssl/ssl_locl.h new file mode 100644 index 0000000..a0c323c --- /dev/null +++ b/src/ssl/ssl_locl.h @@ -0,0 +1,1035 @@ +/* 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 HEADER_SSL_LOCL_H +#define HEADER_SSL_LOCL_H + +#include <openssl/base.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <openssl/aead.h> +#include <openssl/bio.h> +#include <openssl/buf.h> +#include <openssl/dsa.h> +#include <openssl/err.h> +#include <openssl/rsa.h> +#include <openssl/ssl.h> +#include <openssl/stack.h> + + +#define c2l(c, l) \ + (l = ((unsigned long)(*((c)++))), l |= (((unsigned long)(*((c)++))) << 8), \ + l |= (((unsigned long)(*((c)++))) << 16), \ + l |= (((unsigned long)(*((c)++))) << 24)) + +/* NOTE - c is not incremented as per c2l */ +#define c2ln(c, l1, l2, n) \ + { \ + c += n; \ + l1 = l2 = 0; \ + switch (n) { \ + case 8: \ + l2 = ((unsigned long)(*(--(c)))) << 24; \ + case 7: \ + l2 |= ((unsigned long)(*(--(c)))) << 16; \ + case 6: \ + l2 |= ((unsigned long)(*(--(c)))) << 8; \ + case 5: \ + l2 |= ((unsigned long)(*(--(c)))); \ + case 4: \ + l1 = ((unsigned long)(*(--(c)))) << 24; \ + case 3: \ + l1 |= ((unsigned long)(*(--(c)))) << 16; \ + case 2: \ + l1 |= ((unsigned long)(*(--(c)))) << 8; \ + case 1: \ + l1 |= ((unsigned long)(*(--(c)))); \ + } \ + } + +#define l2c(l, c) \ + (*((c)++) = (uint8_t)(((l)) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 8) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 16) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 24) & 0xff)) + +#define n2l(c, l) \ + (l = ((unsigned long)(*((c)++))) << 24, \ + l |= ((unsigned long)(*((c)++))) << 16, \ + l |= ((unsigned long)(*((c)++))) << 8, l |= ((unsigned long)(*((c)++)))) + +#define l2n(l, c) \ + (*((c)++) = (uint8_t)(((l) >> 24) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 16) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 8) & 0xff), \ + *((c)++) = (uint8_t)(((l)) & 0xff)) + +#define l2n8(l, c) \ + (*((c)++) = (uint8_t)(((l) >> 56) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 48) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 40) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 32) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 24) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 16) & 0xff), \ + *((c)++) = (uint8_t)(((l) >> 8) & 0xff), \ + *((c)++) = (uint8_t)(((l)) & 0xff)) + +/* NOTE - c is not incremented as per l2c */ +#define l2cn(l1, l2, c, n) \ + { \ + c += n; \ + switch (n) { \ + case 8: \ + *(--(c)) = (uint8_t)(((l2) >> 24) & 0xff); \ + case 7: \ + *(--(c)) = (uint8_t)(((l2) >> 16) & 0xff); \ + case 6: \ + *(--(c)) = (uint8_t)(((l2) >> 8) & 0xff); \ + case 5: \ + *(--(c)) = (uint8_t)(((l2)) & 0xff); \ + case 4: \ + *(--(c)) = (uint8_t)(((l1) >> 24) & 0xff); \ + case 3: \ + *(--(c)) = (uint8_t)(((l1) >> 16) & 0xff); \ + case 2: \ + *(--(c)) = (uint8_t)(((l1) >> 8) & 0xff); \ + case 1: \ + *(--(c)) = (uint8_t)(((l1)) & 0xff); \ + } \ + } + +#define n2s(c, s) \ + ((s = (((unsigned int)(c[0])) << 8) | (((unsigned int)(c[1])))), c += 2) + +#define s2n(s, c) \ + ((c[0] = (uint8_t)(((s) >> 8) & 0xff), \ + c[1] = (uint8_t)(((s)) & 0xff)), \ + c += 2) + +#define n2l3(c, l) \ + ((l = (((unsigned long)(c[0])) << 16) | (((unsigned long)(c[1])) << 8) | \ + (((unsigned long)(c[2])))), \ + c += 3) + +#define l2n3(l, c) \ + ((c[0] = (uint8_t)(((l) >> 16) & 0xff), \ + c[1] = (uint8_t)(((l) >> 8) & 0xff), \ + c[2] = (uint8_t)(((l)) & 0xff)), \ + c += 3) + +/* LOCAL STUFF */ + +#define SSL_DECRYPT 0 +#define SSL_ENCRYPT 1 + +#define TWO_BYTE_BIT 0x80 +#define SEC_ESC_BIT 0x40 +#define TWO_BYTE_MASK 0x7fff +#define THREE_BYTE_MASK 0x3fff + +#define INC32(a) ((a) = ((a) + 1) & 0xffffffffL) +#define DEC32(a) ((a) = ((a)-1) & 0xffffffffL) +#define MAX_MAC_SIZE 20 /* up from 16 for SSLv3 */ + +/* Define the Bitmasks for SSL_CIPHER.algorithms. + * + * This bits are used packed as dense as possible. If new methods/ciphers etc + * will be added, the bits a likely to change, so this information is for + * internal library use only, even though SSL_CIPHER.algorithms can be publicly + * accessed. Use the according functions for cipher management instead. + * + * The bit mask handling in the selection and sorting scheme in + * ssl_create_cipher_list() has only limited capabilities, reflecting that the + * different entities within are mutually exclusive: + * ONLY ONE BIT PER MASK CAN BE SET AT A TIME. */ + +/* Bits for algorithm_mkey (key exchange algorithm) */ +#define SSL_kRSA 0x00000001L /* RSA key exchange */ +#define SSL_kEDH 0x00000002L /* tmp DH key no DH cert */ +#define SSL_kEECDH 0x00000004L /* ephemeral ECDH */ +#define SSL_kPSK 0x00000008L /* PSK */ + +/* Bits for algorithm_auth (server authentication) */ +#define SSL_aRSA 0x00000001L /* RSA auth */ +#define SSL_aNULL 0x00000002L /* no auth (i.e. use ADH or AECDH) */ +#define SSL_aECDSA 0x00000004L /* ECDSA auth*/ +#define SSL_aPSK 0x00000008L /* PSK auth */ + +/* Bits for algorithm_enc (symmetric encryption) */ +#define SSL_3DES 0x00000001L +#define SSL_RC4 0x00000002L +#define SSL_AES128 0x00000004L +#define SSL_AES256 0x00000008L +#define SSL_AES128GCM 0x00000010L +#define SSL_AES256GCM 0x00000020L +#define SSL_CHACHA20POLY1305 0x00000040L + +#define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM) + +/* Bits for algorithm_mac (symmetric authentication) */ + +#define SSL_MD5 0x00000001L +#define SSL_SHA1 0x00000002L +#define SSL_SHA256 0x00000004L +#define SSL_SHA384 0x00000008L +/* Not a real MAC, just an indication it is part of cipher */ +#define SSL_AEAD 0x00000010L + +/* Bits for algorithm_ssl (protocol version) */ +#define SSL_SSLV3 0x00000002L +#define SSL_TLSV1 SSL_SSLV3 /* for now */ +#define SSL_TLSV1_2 0x00000004L + +/* Bits for algorithm2 (handshake digests and other extra flags) */ + +#define SSL_HANDSHAKE_MAC_MD5 0x10 +#define SSL_HANDSHAKE_MAC_SHA 0x20 +#define SSL_HANDSHAKE_MAC_SHA256 0x40 +#define SSL_HANDSHAKE_MAC_SHA384 0x80 +#define SSL_HANDSHAKE_MAC_DEFAULT \ + (SSL_HANDSHAKE_MAC_MD5 | SSL_HANDSHAKE_MAC_SHA) + +/* When adding new digest in the ssl_ciph.c and increment SSM_MD_NUM_IDX + * make sure to update this constant too */ +#define SSL_MAX_DIGEST 4 + +#define TLS1_PRF_DGST_MASK (0xff << TLS1_PRF_DGST_SHIFT) + +#define TLS1_PRF_DGST_SHIFT 10 +#define TLS1_PRF_MD5 (SSL_HANDSHAKE_MAC_MD5 << TLS1_PRF_DGST_SHIFT) +#define TLS1_PRF_SHA1 (SSL_HANDSHAKE_MAC_SHA << TLS1_PRF_DGST_SHIFT) +#define TLS1_PRF_SHA256 (SSL_HANDSHAKE_MAC_SHA256 << TLS1_PRF_DGST_SHIFT) +#define TLS1_PRF_SHA384 (SSL_HANDSHAKE_MAC_SHA384 << TLS1_PRF_DGST_SHIFT) +#define TLS1_PRF (TLS1_PRF_MD5 | TLS1_PRF_SHA1) + +#define TLSEXT_CHANNEL_ID_SIZE 128 + +/* SSL_CIPHER_ALGORITHM2_AEAD is a flag in SSL_CIPHER.algorithm2 which + * indicates that the cipher is implemented via an EVP_AEAD. */ +#define SSL_CIPHER_ALGORITHM2_AEAD (1 << 23) + +/* SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD is a flag in + * SSL_CIPHER.algorithm2 which indicates that the variable part of the nonce is + * included as a prefix of the record. (AES-GCM, for example, does with with an + * 8-byte variable nonce.) */ +#define SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD (1<<22) + +/* Cipher strength information. */ +#define SSL_MEDIUM 0x00000001L +#define SSL_HIGH 0x00000002L +#define SSL_FIPS 0x00000004L + +/* we have used 000001ff - 23 bits left to go */ + +/* Check if an SSL structure is using DTLS */ +#define SSL_IS_DTLS(s) (s->enc_method->enc_flags & SSL_ENC_FLAG_DTLS) +/* See if we need explicit IV */ +#define SSL_USE_EXPLICIT_IV(s) \ + (s->enc_method->enc_flags & SSL_ENC_FLAG_EXPLICIT_IV) +/* See if we use signature algorithms extension and signature algorithm before + * signatures. */ +#define SSL_USE_SIGALGS(s) (s->enc_method->enc_flags & SSL_ENC_FLAG_SIGALGS) +/* Allow TLS 1.2 ciphersuites: applies to DTLS 1.2 as well as TLS 1.2: may + * apply to others in future. */ +#define SSL_USE_TLS1_2_CIPHERS(s) \ + (s->enc_method->enc_flags & SSL_ENC_FLAG_TLS1_2_CIPHERS) +/* Determine if a client can use TLS 1.2 ciphersuites: can't rely on method + * flags because it may not be set to correct version yet. */ +#define SSL_CLIENT_USE_TLS1_2_CIPHERS(s) \ + ((SSL_IS_DTLS(s) && s->client_version <= DTLS1_2_VERSION) || \ + (!SSL_IS_DTLS(s) && s->client_version >= TLS1_2_VERSION)) + +/* Mostly for SSLv3 */ +#define SSL_PKEY_RSA_ENC 0 +#define SSL_PKEY_RSA_SIGN 1 +#define SSL_PKEY_ECC 2 +#define SSL_PKEY_NUM 3 + +/* SSL_kRSA <- RSA_ENC | (RSA_TMP & RSA_SIGN) | + * <- (EXPORT & (RSA_ENC | RSA_TMP) & RSA_SIGN) + * SSL_kDH <- DH_ENC & (RSA_ENC | RSA_SIGN | DSA_SIGN) + * SSL_kEDH <- RSA_ENC | RSA_SIGN | DSA_SIGN + * SSL_aRSA <- RSA_ENC | RSA_SIGN + * SSL_aDSS <- DSA_SIGN */ + +#define PENDING_SESSION -10000 +#define CERTIFICATE_SELECTION_PENDING -10001 + +/* From RFC4492, used in encoding the curve type in ECParameters */ +#define EXPLICIT_PRIME_CURVE_TYPE 1 +#define EXPLICIT_CHAR2_CURVE_TYPE 2 +#define NAMED_CURVE_TYPE 3 + +/* Values for the |hash_message| parameter of |s->method->ssl_get_message|. */ +#define SSL_GET_MESSAGE_DONT_HASH_MESSAGE 0 +#define SSL_GET_MESSAGE_HASH_MESSAGE 1 + +typedef struct cert_pkey_st { + X509 *x509; + EVP_PKEY *privatekey; + /* Chain for this certificate */ + STACK_OF(X509) * chain; +} CERT_PKEY; + +typedef struct cert_st { + /* Current active set */ + CERT_PKEY *key; /* ALWAYS points to an element of the pkeys array + * Probably it would make more sense to store + * an index, not a pointer. */ + + /* For clients the following masks are of *disabled* key and auth algorithms + * based on the current session. + * + * TODO(davidben): Remove these. They get checked twice: when sending the + * ClientHello and when processing the ServerHello. However, mask_ssl is a + * different value both times. mask_k and mask_a are not, but is a + * round-about way of checking the server's cipher was one of the advertised + * ones. (Currently it checks the masks and then the list of ciphers prior to + * applying the masks in ClientHello.) */ + unsigned long mask_k; + unsigned long mask_a; + unsigned long mask_ssl; + + DH *dh_tmp; + DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize); + EC_KEY *ecdh_tmp; + /* Callback for generating ephemeral ECDH keys */ + EC_KEY *(*ecdh_tmp_cb)(SSL *ssl, int is_export, int keysize); + /* Select ECDH parameters automatically */ + int ecdh_tmp_auto; + /* Flags related to certificates */ + unsigned int cert_flags; + CERT_PKEY pkeys[SSL_PKEY_NUM]; + + /* Server-only: client_certificate_types is list of certificate types to + * include in the CertificateRequest message. + */ + uint8_t *client_certificate_types; + size_t num_client_certificate_types; + + /* signature algorithms peer reports: e.g. supported signature + * algorithms extension for server or as part of a certificate + * request for client. */ + uint8_t *peer_sigalgs; + /* Size of above array */ + size_t peer_sigalgslen; + /* suppported signature algorithms. + * When set on a client this is sent in the client hello as the + * supported signature algorithms extension. For servers + * it represents the signature algorithms we are willing to use. */ + uint8_t *conf_sigalgs; + /* Size of above array */ + size_t conf_sigalgslen; + /* Client authentication signature algorithms, if not set then + * uses conf_sigalgs. On servers these will be the signature + * algorithms sent to the client in a cerificate request for TLS 1.2. + * On a client this represents the signature algortithms we are + * willing to use for client authentication. */ + uint8_t *client_sigalgs; + /* Size of above array */ + size_t client_sigalgslen; + /* Signature algorithms shared by client and server: cached + * because these are used most often. */ + TLS_SIGALGS *shared_sigalgs; + size_t shared_sigalgslen; + + /* 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 chain building or certificate validation + * If NULL the parent SSL_CTX store is used instead. */ + X509_STORE *chain_store; + X509_STORE *verify_store; + + /* Raw values of the cipher list from a client */ + uint8_t *ciphers_raw; + size_t ciphers_rawlen; +} CERT; + +typedef struct sess_cert_st { + STACK_OF(X509) * cert_chain; /* as received from peer (not for SSL2) */ + + /* The 'peer_...' members are used only by clients. */ + int peer_cert_type; + + CERT_PKEY *peer_key; /* points to an element of peer_pkeys (never NULL!) */ + CERT_PKEY peer_pkeys[SSL_PKEY_NUM]; + /* Obviously we don't have the private keys of these, + * so maybe we shouldn't even use the CERT_PKEY type here. */ + + DH *peer_dh_tmp; + EC_KEY *peer_ecdh_tmp; +} SESS_CERT; + +/* Structure containing decoded values of signature algorithms extension */ +struct tls_sigalgs_st { + /* NID of hash algorithm */ + int hash_nid; + /* NID of signature algorithm */ + int sign_nid; + /* Combined hash and signature NID */ + int signandhash_nid; + /* Raw values used in extension */ + uint8_t rsign; + uint8_t rhash; +}; + +/* 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; +}; + +/* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */ +struct ssl_protocol_method_st { + int (*ssl_new)(SSL *s); + void (*ssl_free)(SSL *s); + int (*ssl_accept)(SSL *s); + int (*ssl_connect)(SSL *s); + int (*ssl_read)(SSL *s, void *buf, int len); + int (*ssl_peek)(SSL *s, void *buf, int len); + int (*ssl_write)(SSL *s, const void *buf, int len); + int (*ssl_shutdown)(SSL *s); + int (*ssl_renegotiate)(SSL *s); + int (*ssl_renegotiate_check)(SSL *s); + long (*ssl_get_message)(SSL *s, int header_state, int body_state, + int msg_type, long max, int hash_message, int *ok); + int (*ssl_read_bytes)(SSL *s, int type, uint8_t *buf, int len, int peek); + int (*ssl_write_bytes)(SSL *s, int type, const void *buf_, int len); + int (*ssl_dispatch_alert)(SSL *s); + long (*ssl_ctrl)(SSL *s, int cmd, long larg, void *parg); + long (*ssl_ctx_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg); + int (*ssl_pending)(const SSL *s); + int (*num_ciphers)(void); + const SSL_CIPHER *(*get_cipher)(unsigned ncipher); + int (*ssl_version)(void); + long (*ssl_callback_ctrl)(SSL *s, int cb_id, void (*fp)(void)); + long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)(void)); +}; + +/* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit + * of a mess of functions, but hell, think of it as an opaque structure. */ +struct ssl3_enc_method { + int (*enc)(SSL *, int); + int (*prf)(SSL *, uint8_t *, size_t, const uint8_t *, size_t, const char *, + size_t, const uint8_t *, size_t, const uint8_t *, size_t); + int (*setup_key_block)(SSL *); + int (*generate_master_secret)(SSL *, uint8_t *, const uint8_t *, size_t); + int (*change_cipher_state)(SSL *, int); + int (*final_finish_mac)(SSL *, const char *, int, uint8_t *); + int finish_mac_length; + int (*cert_verify_mac)(SSL *, int, uint8_t *); + const char *client_finished_label; + int client_finished_label_len; + const char *server_finished_label; + int server_finished_label_len; + int (*alert_value)(int); + int (*export_keying_material)(SSL *, uint8_t *, size_t, const char *, size_t, + const uint8_t *, size_t, int use_context); + /* Various flags indicating protocol version requirements */ + unsigned int enc_flags; + /* Handshake header length */ + unsigned int hhlen; + /* Set the handshake header */ + void (*set_handshake_header)(SSL *s, int type, unsigned long len); + /* Write out handshake message */ + int (*do_write)(SSL *s); +}; + +#define SSL_HM_HEADER_LENGTH(s) s->enc_method->hhlen +#define ssl_handshake_start(s) \ + (((uint8_t *)s->init_buf->data) + s->enc_method->hhlen) +#define ssl_set_handshake_header(s, htype, len) \ + s->enc_method->set_handshake_header(s, htype, len) +#define ssl_do_write(s) s->enc_method->do_write(s) + +/* Values for enc_flags */ + +/* Uses explicit IV for CBC mode */ +#define SSL_ENC_FLAG_EXPLICIT_IV 0x1 +/* Uses signature algorithms extension */ +#define SSL_ENC_FLAG_SIGALGS 0x2 +/* Uses SHA256 default PRF */ +#define SSL_ENC_FLAG_SHA256_PRF 0x4 +/* Is DTLS */ +#define SSL_ENC_FLAG_DTLS 0x8 +/* Allow TLS 1.2 ciphersuites: applies to DTLS 1.2 as well as TLS 1.2: + * may apply to others in future. */ +#define SSL_ENC_FLAG_TLS1_2_CIPHERS 0x10 + +/* ssl_aead_ctx_st contains information about an AEAD that is being used to + * encrypt an SSL connection. */ +struct ssl_aead_ctx_st { + EVP_AEAD_CTX ctx; + /* fixed_nonce contains any bytes of the nonce that are fixed for all + * records. */ + uint8_t fixed_nonce[8]; + uint8_t fixed_nonce_len, variable_nonce_len, tag_len; + /* variable_nonce_included_in_record is non-zero if the variable nonce + * for a record is included as a prefix before the ciphertext. */ + char variable_nonce_included_in_record; + /* random_variable_nonce is non-zero if the variable nonce is + * randomly generated, rather than derived from the sequence + * number. */ + char random_variable_nonce; + /* omit_length_in_ad is non-zero if the length should be omitted in the + * AEAD's ad parameter. */ + char omit_length_in_ad; + /* omit_version_in_ad is non-zero if the version should be omitted + * in the AEAD's ad parameter. */ + char omit_version_in_ad; +}; + +extern const SSL_CIPHER ssl3_ciphers[]; + +extern const SSL3_ENC_METHOD TLSv1_enc_data; +extern const SSL3_ENC_METHOD TLSv1_1_enc_data; +extern const SSL3_ENC_METHOD TLSv1_2_enc_data; +extern const SSL3_ENC_METHOD SSLv3_enc_data; +extern const SSL3_ENC_METHOD DTLSv1_enc_data; +extern const SSL3_ENC_METHOD DTLSv1_2_enc_data; + +void ssl_clear_cipher_ctx(SSL *s); +int ssl_clear_bad_session(SSL *s); +CERT *ssl_cert_new(void); +CERT *ssl_cert_dup(CERT *cert); +int ssl_cert_inst(CERT **o); +void ssl_cert_clear_certs(CERT *c); +void ssl_cert_free(CERT *c); +SESS_CERT *ssl_sess_cert_new(void); +void ssl_sess_cert_free(SESS_CERT *sc); +int ssl_set_peer_cert_type(SESS_CERT *c, int type); +int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx); +int ssl_cipher_id_cmp(const void *in_a, const void *in_b); +int ssl_cipher_ptr_id_cmp(const SSL_CIPHER **ap, const SSL_CIPHER **bp); +STACK_OF(SSL_CIPHER) * ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs); +int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) * sk, uint8_t *p); +STACK_OF(SSL_CIPHER) * + ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *meth, + struct ssl_cipher_preference_list_st **pref, + STACK_OF(SSL_CIPHER) * *sorted, const char *rule_str, + CERT *c); +struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_dup( + struct ssl_cipher_preference_list_st *cipher_list); +void ssl_cipher_preference_list_free( + struct ssl_cipher_preference_list_st *cipher_list); +struct ssl_cipher_preference_list_st *ssl_cipher_preference_list_from_ciphers( + STACK_OF(SSL_CIPHER) * ciphers); +struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *s); + +/* 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); + +int ssl_get_handshake_digest(size_t i, long *mask, const EVP_MD **md); +int ssl_cipher_get_cert_index(const SSL_CIPHER *c); +int ssl_cipher_has_server_public_key(const SSL_CIPHER *cipher); +int ssl_cipher_requires_server_key_exchange(const SSL_CIPHER *cipher); + +int ssl_cert_set0_chain(CERT *c, STACK_OF(X509) * chain); +int ssl_cert_set1_chain(CERT *c, STACK_OF(X509) * chain); +int ssl_cert_add0_chain_cert(CERT *c, X509 *x); +int ssl_cert_add1_chain_cert(CERT *c, X509 *x); +int ssl_cert_select_current(CERT *c, X509 *x); +void ssl_cert_set_cert_cb(CERT *c, int (*cb)(SSL *ssl, void *arg), void *arg); + +int ssl_verify_cert_chain(SSL *s, STACK_OF(X509) * sk); +int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l); +int ssl_build_cert_chain(CERT *c, X509_STORE *chain_store, int flags); +int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain, int ref); +int ssl_undefined_function(SSL *s); +int ssl_undefined_void_function(void); +int ssl_undefined_const_function(const SSL *s); +CERT_PKEY *ssl_get_server_send_pkey(const SSL *s); +EVP_PKEY *ssl_get_sign_pkey(SSL *s, const SSL_CIPHER *c); +int ssl_cert_type(EVP_PKEY *pkey); + +/* ssl_get_compatible_server_ciphers determines the key exchange and + * authentication cipher suite masks compatible with the server configuration + * and current ClientHello parameters of |s|. It sets |*out_mask_k| to the key + * exchange mask and |*out_mask_a| to the authentication mask. */ +void ssl_get_compatible_server_ciphers(SSL *s, unsigned long *out_mask_k, + unsigned long *out_mask_a); + +STACK_OF(SSL_CIPHER) * ssl_get_ciphers_by_id(SSL *s); +int ssl_verify_alarm_type(long type); +int ssl_fill_hello_random(SSL *s, int server, uint8_t *field, size_t len); + +const SSL_CIPHER *ssl3_get_cipher_by_value(uint16_t value); +uint16_t ssl3_get_cipher_value(const SSL_CIPHER *c); +int ssl3_init_finished_mac(SSL *s); +int ssl3_send_server_certificate(SSL *s); +int ssl3_send_new_session_ticket(SSL *s); +int ssl3_send_cert_status(SSL *s); +int ssl3_get_finished(SSL *s, int state_a, int state_b); +int ssl3_send_change_cipher_spec(SSL *s, int state_a, int state_b); +int ssl3_prf(SSL *s, 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); +void ssl3_cleanup_key_block(SSL *s); +int ssl3_do_write(SSL *s, int type); +int ssl3_send_alert(SSL *s, int level, int desc); +int ssl3_get_req_cert_type(SSL *s, uint8_t *p); +long ssl3_get_message(SSL *s, int header_state, int body_state, int msg_type, + long max, int hash_message, int *ok); + +/* ssl3_hash_current_message incorporates the current handshake message into + * the handshake hash. */ +void ssl3_hash_current_message(SSL *s); + +/* ssl3_cert_verify_hash writes the 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. For TLS 1.2 and up, |*out_md| is used + * for the hash function, otherwise the hash function depends on the type of + * |pkey| and is written to |*out_md|. It returns one on success and zero on + * failure. */ +int ssl3_cert_verify_hash(SSL *s, uint8_t *out, size_t *out_len, + const EVP_MD **out_md, EVP_PKEY *pkey); + +int ssl3_send_finished(SSL *s, int a, int b, const char *sender, int slen); +int ssl3_num_ciphers(void); +const SSL_CIPHER *ssl3_get_cipher(unsigned int u); +int ssl3_renegotiate(SSL *ssl); +int ssl3_renegotiate_check(SSL *ssl); +int ssl3_dispatch_alert(SSL *s); +int ssl3_expect_change_cipher_spec(SSL *s); +int ssl3_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek); +int ssl3_write_bytes(SSL *s, int type, const void *buf, int len); +int ssl3_final_finish_mac(SSL *s, const char *sender, int slen, uint8_t *p); +int ssl3_cert_verify_mac(SSL *s, int md_nid, uint8_t *p); +void ssl3_finish_mac(SSL *s, const uint8_t *buf, int len); +void ssl3_free_digest_list(SSL *s); +unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk); +const SSL_CIPHER *ssl3_choose_cipher( + SSL *ssl, STACK_OF(SSL_CIPHER) * clnt, + struct ssl_cipher_preference_list_st *srvr); +int ssl3_setup_buffers(SSL *s); +int ssl3_setup_read_buffer(SSL *s); +int ssl3_setup_write_buffer(SSL *s); +int ssl3_release_read_buffer(SSL *s); +int ssl3_release_write_buffer(SSL *s); + +enum should_free_handshake_buffer_t { + free_handshake_buffer, + dont_free_handshake_buffer, +}; +int ssl3_digest_cached_records(SSL *s, enum should_free_handshake_buffer_t); + +int ssl3_new(SSL *s); +void ssl3_free(SSL *s); +int ssl3_accept(SSL *s); +int ssl3_connect(SSL *s); +int ssl3_read(SSL *s, void *buf, int len); +int ssl3_peek(SSL *s, void *buf, int len); +int ssl3_write(SSL *s, const void *buf, int len); +int ssl3_shutdown(SSL *s); +long ssl3_ctrl(SSL *s, int cmd, long larg, void *parg); +long ssl3_ctx_ctrl(SSL_CTX *s, int cmd, long larg, void *parg); +long ssl3_callback_ctrl(SSL *s, int cmd, void (*fp)(void)); +long ssl3_ctx_callback_ctrl(SSL_CTX *s, int cmd, void (*fp)(void)); +int ssl3_pending(const SSL *s); + +void ssl3_record_sequence_update(uint8_t *seq); +int ssl3_do_change_cipher_spec(SSL *ssl); + +void ssl3_set_handshake_header(SSL *s, int htype, unsigned long len); +int ssl3_handshake_write(SSL *s); + +int dtls1_do_write(SSL *s, int type); +int ssl3_read_n(SSL *s, int n, int max, int extend); +int dtls1_read_bytes(SSL *s, int type, uint8_t *buf, int len, int peek); +int ssl3_write_pending(SSL *s, int type, const uint8_t *buf, unsigned int len); +void dtls1_set_message_header(SSL *s, uint8_t mt, unsigned long len, + unsigned short seq_num, unsigned long frag_off, + unsigned long frag_len); + +int dtls1_write_app_data_bytes(SSL *s, int type, const void *buf, int len); +int dtls1_write_bytes(SSL *s, int type, const void *buf, int len); + +int dtls1_send_change_cipher_spec(SSL *s, int a, int b); +int dtls1_send_finished(SSL *s, int a, int b, const char *sender, int slen); +int dtls1_read_failed(SSL *s, int code); +int dtls1_buffer_message(SSL *s, int ccs); +int dtls1_retransmit_message(SSL *s, unsigned short seq, unsigned long frag_off, + int *found); +int dtls1_get_queue_priority(unsigned short seq, int is_ccs); +int dtls1_retransmit_buffered_messages(SSL *s); +void dtls1_clear_record_buffer(SSL *s); +void dtls1_get_message_header(uint8_t *data, struct hm_header_st *msg_hdr); +void dtls1_get_ccs_header(uint8_t *data, struct ccs_header_st *ccs_hdr); +void dtls1_reset_seq_numbers(SSL *s, int rw); +int dtls1_check_timeout_num(SSL *s); +int dtls1_handle_timeout(SSL *s); +const SSL_CIPHER *dtls1_get_cipher(unsigned int u); +void dtls1_start_timer(SSL *s); +void dtls1_stop_timer(SSL *s); +int dtls1_is_timer_expired(SSL *s); +void dtls1_double_timeout(SSL *s); +unsigned int dtls1_min_mtu(void); +void dtls1_hm_fragment_free(hm_fragment *frag); + +/* some client-only functions */ +int ssl3_send_client_hello(SSL *s); +int ssl3_get_server_hello(SSL *s); +int ssl3_get_certificate_request(SSL *s); +int ssl3_get_new_session_ticket(SSL *s); +int ssl3_get_cert_status(SSL *s); +int ssl3_get_server_done(SSL *s); +int ssl3_send_cert_verify(SSL *s); +int ssl3_send_client_certificate(SSL *s); +int ssl_do_client_cert_cb(SSL *s, X509 **px509, EVP_PKEY **ppkey); +int ssl3_send_client_key_exchange(SSL *s); +int ssl3_get_server_key_exchange(SSL *s); +int ssl3_get_server_certificate(SSL *s); +int ssl3_check_cert_and_algorithm(SSL *s); +int ssl3_send_next_proto(SSL *s); +int ssl3_send_channel_id(SSL *s); + +int dtls1_client_hello(SSL *s); + +/* some server-only functions */ +int ssl3_get_initial_bytes(SSL *s); +int ssl3_get_v2_client_hello(SSL *s); +int ssl3_get_client_hello(SSL *s); +int ssl3_send_server_hello(SSL *s); +int ssl3_send_hello_request(SSL *s); +int ssl3_send_server_key_exchange(SSL *s); +int ssl3_send_certificate_request(SSL *s); +int ssl3_send_server_done(SSL *s); +int ssl3_get_client_certificate(SSL *s); +int ssl3_get_client_key_exchange(SSL *s); +int ssl3_get_cert_verify(SSL *s); +int ssl3_get_next_proto(SSL *s); +int ssl3_get_channel_id(SSL *s); + +int dtls1_new(SSL *s); +int dtls1_accept(SSL *s); +int dtls1_connect(SSL *s); +void dtls1_free(SSL *s); +long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg); +int dtls1_shutdown(SSL *s); + +long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, + int hash_message, int *ok); +int dtls1_get_record(SSL *s); +int dtls1_dispatch_alert(SSL *s); + +int ssl_init_wbio_buffer(SSL *s, int push); +void ssl_free_wbio_buffer(SSL *s); + +/* tls1_prf computes the TLS PRF function for |s| as described in RFC 5246, + * section 5 and RFC 2246 section 5. 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(SSL *s, 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); + +int tls1_change_cipher_state(SSL *s, int which); +int tls1_setup_key_block(SSL *s); +int tls1_enc(SSL *s, int snd); +int tls1_handshake_digest(SSL *s, uint8_t *out, size_t out_len); +int tls1_final_finish_mac(SSL *s, const char *str, int slen, uint8_t *p); +int tls1_cert_verify_mac(SSL *s, int md_nid, uint8_t *p); +int tls1_generate_master_secret(SSL *s, uint8_t *out, const uint8_t *premaster, + size_t premaster_len); +int tls1_export_keying_material(SSL *s, uint8_t *out, size_t olen, + const char *label, size_t llen, + const uint8_t *p, size_t plen, int use_context); +int tls1_alert_code(int code); +int ssl3_alert_code(int code); +int ssl_ok(SSL *s); + +int ssl_check_srvr_ecc_cert_and_alg(X509 *x, SSL *s); + +char ssl_early_callback_init(struct ssl_early_callback_ctx *ctx); +int tls1_ec_curve_id2nid(uint16_t curve_id); +int tls1_ec_nid2curve_id(uint16_t *out_curve_id, int nid); + +/* tls1_check_curve parses ECParameters out of |cbs|, modifying it. It + * checks the curve is one of our preferences and writes the + * NamedCurve value to |*out_curve_id|. It returns one on success and + * zero on error. */ +int tls1_check_curve(SSL *s, CBS *cbs, uint16_t *out_curve_id); + +/* tls1_get_shared_curve returns the NID of the first preferred shared curve + * between client and server preferences. If none can be found, it returns + * NID_undef. */ +int tls1_get_shared_curve(SSL *s); + +/* tls1_set_curves converts the array of |ncurves| NIDs pointed to by |curves| + * into a newly allocated array of TLS curve IDs. On success, the function + * returns one and writes the array to |*out_curve_ids| and its size to + * |*out_curve_ids_len|. Otherwise, it returns zero. */ +int tls1_set_curves(uint16_t **out_curve_ids, size_t *out_curve_ids_len, + const int *curves, size_t ncurves); + +/* tls1_check_ec_cert returns one if |x| is an ECC certificate with curve and + * point format compatible with the client's preferences. Otherwise it returns + * zero. */ +int tls1_check_ec_cert(SSL *s, X509 *x); + +/* tls1_check_ec_tmp_key returns one if the EC temporary key is compatible with + * client extensions and zero otherwise. */ +int tls1_check_ec_tmp_key(SSL *s); + +int tls1_shared_list(SSL *s, const uint8_t *l1, size_t l1len, const uint8_t *l2, + size_t l2len, int nmatch); +uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *buf, uint8_t *limit, + size_t header_len); +uint8_t *ssl_add_serverhello_tlsext(SSL *s, uint8_t *buf, uint8_t *limit); +int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs); +int ssl_parse_serverhello_tlsext(SSL *s, CBS *cbs); +int ssl_prepare_clienthello_tlsext(SSL *s); +int ssl_prepare_serverhello_tlsext(SSL *s); + +#define tlsext_tick_md EVP_sha256 +int tls1_process_ticket(SSL *s, const struct ssl_early_callback_ctx *ctx, + SSL_SESSION **ret); + +int tls12_get_sigandhash(uint8_t *p, const EVP_PKEY *pk, const EVP_MD *md); +int tls12_get_sigid(const EVP_PKEY *pk); +const EVP_MD *tls12_get_hash(uint8_t hash_alg); + +int tls1_channel_id_hash(EVP_MD_CTX *ctx, SSL *s); +int tls1_record_handshake_hashes_for_channel_id(SSL *s); + +int tls1_set_sigalgs_list(CERT *c, const char *str, int client); +int tls1_set_sigalgs(CERT *c, const int *salg, size_t salglen, int client); + +/* ssl_ctx_log_rsa_client_key_exchange logs |premaster| to |ctx|, if logging is + * enabled. It returns one on success and zero on failure. The entry is + * identified by the first 8 bytes of |encrypted_premaster|. */ +int ssl_ctx_log_rsa_client_key_exchange(SSL_CTX *ctx, + const uint8_t *encrypted_premaster, + size_t encrypted_premaster_len, + const uint8_t *premaster, + size_t premaster_len); + +/* ssl_ctx_log_master_secret logs |master| to |ctx|, if logging is enabled. It + * returns one on success and zero on failure. The entry is identified by + * |client_random|. */ +int ssl_ctx_log_master_secret(SSL_CTX *ctx, const uint8_t *client_random, + size_t client_random_len, const uint8_t *master, + size_t master_len); + +int ssl3_can_cutthrough(const SSL *s); + +/* ssl3_get_enc_method returns the SSL3_ENC_METHOD corresponding to + * |version|. */ +const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version); + +/* ssl3_get_max_server_version returns the maximum SSL/TLS version number + * supported by |s| as a server, or zero if all versions are disabled. */ +uint16_t ssl3_get_max_server_version(const SSL *s); + +/* ssl3_get_mutual_version selects the protocol version on |s| for a client + * which advertises |client_version|. If no suitable version exists, it returns + * zero. */ +uint16_t ssl3_get_mutual_version(SSL *s, uint16_t client_version); + +/* ssl3_get_max_client_version returns the maximum protocol version configured + * for the client. It is guaranteed that the set of allowed versions at or below + * this maximum version is contiguous. If all versions are disabled, it returns + * zero. */ +uint16_t ssl3_get_max_client_version(SSL *s); + +/* ssl3_is_version_enabled returns one if |version| is an enabled protocol + * version for |s| and zero otherwise. */ +int ssl3_is_version_enabled(SSL *s, uint16_t version); + +/* ssl3_version_from_wire maps |wire_version| to a protocol version. For + * SSLv3/TLS, the version is returned as-is. For DTLS, the corresponding TLS + * version is used. Note that this mapping is not injective but preserves + * comparisons. + * + * TODO(davidben): To normalize some DTLS-specific code, move away from using + * the wire version except at API boundaries. */ +uint16_t ssl3_version_from_wire(SSL *s, uint16_t wire_version); + +int ssl_add_serverhello_renegotiate_ext(SSL *s, uint8_t *p, int *len, + int maxlen); +int ssl_parse_serverhello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert); +int ssl_add_clienthello_renegotiate_ext(SSL *s, uint8_t *p, int *len, + int maxlen); +int ssl_parse_clienthello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert); +long ssl_get_algorithm2(SSL *s); +int tls1_process_sigalgs(SSL *s, const CBS *sigalgs); + +/* tls1_choose_signing_digest returns a digest for use with |pkey| based on the + * peer's preferences recorded for |s| and the digests supported by |pkey|. */ +const EVP_MD *tls1_choose_signing_digest(SSL *s, EVP_PKEY *pkey); + +size_t tls12_get_psigalgs(SSL *s, const uint8_t **psigs); +int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s, + CBS *cbs, EVP_PKEY *pkey); +void ssl_set_client_disabled(SSL *s); + +int ssl_add_clienthello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen); +int ssl_parse_clienthello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert); +int ssl_add_serverhello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen); +int ssl_parse_serverhello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert); + +#endif diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c new file mode 100644 index 0000000..3d1bc62 --- /dev/null +++ b/src/ssl/ssl_rsa.c @@ -0,0 +1,707 @@ +/* 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 <stdio.h> + +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/pem.h> +#include <openssl/x509.h> + +#include "ssl_locl.h" + +static int ssl_set_cert(CERT *c, X509 *x509); +static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey); + +int SSL_use_certificate(SSL *ssl, X509 *x) { + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_certificate, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (!ssl_cert_inst(&ssl->cert)) { + OPENSSL_PUT_ERROR(SSL, SSL_use_certificate, ERR_R_MALLOC_FAILURE); + return 0; + } + return ssl_set_cert(ssl->cert, x); +} + +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, SSL_use_certificate_file, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_use_certificate_file, 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_use_certificate_file, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_certificate_file, reason_code); + goto end; + } + + ret = SSL_use_certificate(ssl, x); + +end: + if (x != NULL) { + X509_free(x); + } + if (in != NULL) { + BIO_free(in); + } + + return ret; +} + +int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *d, int len) { + X509 *x; + int ret; + + x = d2i_X509(NULL, &d, (long)len); + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_certificate_ASN1, ERR_R_ASN1_LIB); + return 0; + } + + ret = SSL_use_certificate(ssl, x); + X509_free(x); + return ret; +} + +int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) { + EVP_PKEY *pkey; + int ret; + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (!ssl_cert_inst(&ssl->cert)) { + OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey, ERR_R_MALLOC_FAILURE); + return 0; + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey, 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; +} + +static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey) { + int i; + + i = ssl_cert_type(pkey); + if (i < 0) { + OPENSSL_PUT_ERROR(SSL, ssl_set_pkey, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + return 0; + } + + if (c->pkeys[i].x509 != NULL) { + EVP_PKEY *pktmp; + pktmp = X509_get_pubkey(c->pkeys[i].x509); + EVP_PKEY_copy_parameters(pktmp, pkey); + EVP_PKEY_free(pktmp); + ERR_clear_error(); + + /* Sanity-check that the private key and the certificate match, unless the + * key is opaque (in case of, say, a smartcard). */ + if (!EVP_PKEY_is_opaque(pkey) && + !X509_check_private_key(c->pkeys[i].x509, pkey)) { + X509_free(c->pkeys[i].x509); + c->pkeys[i].x509 = NULL; + return 0; + } + } + + if (c->pkeys[i].privatekey != NULL) { + EVP_PKEY_free(c->pkeys[i].privatekey); + } + c->pkeys[i].privatekey = EVP_PKEY_dup(pkey); + c->key = &(c->pkeys[i]); + + return 1; +} + +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, SSL_use_RSAPrivateKey_file, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey_file, 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_use_RSAPrivateKey_file, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey_file, reason_code); + goto end; + } + ret = SSL_use_RSAPrivateKey(ssl, rsa); + RSA_free(rsa); + +end: + if (in != NULL) { + BIO_free(in); + } + return ret; +} + +int SSL_use_RSAPrivateKey_ASN1(SSL *ssl, uint8_t *d, long len) { + int ret; + const uint8_t *p; + RSA *rsa; + + p = d; + rsa = d2i_RSAPrivateKey(NULL, &p, (long)len); + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_RSAPrivateKey_ASN1, ERR_R_ASN1_LIB); + return 0; + } + + ret = SSL_use_RSAPrivateKey(ssl, rsa); + RSA_free(rsa); + return ret; +} + +int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) { + int ret; + + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (!ssl_cert_inst(&ssl->cert)) { + OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey, ERR_R_MALLOC_FAILURE); + return 0; + } + + ret = ssl_set_pkey(ssl->cert, pkey); + 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, SSL_use_PrivateKey_file, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey_file, 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_use_PrivateKey_file, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey_file, reason_code); + goto end; + } + ret = SSL_use_PrivateKey(ssl, pkey); + EVP_PKEY_free(pkey); + +end: + if (in != NULL) { + BIO_free(in); + } + return ret; +} + +int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *d, long len) { + int ret; + const uint8_t *p; + EVP_PKEY *pkey; + + p = d; + pkey = d2i_PrivateKey(type, NULL, &p, (long)len); + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_use_PrivateKey_ASN1, ERR_R_ASN1_LIB); + return 0; + } + + ret = SSL_use_PrivateKey(ssl, pkey); + EVP_PKEY_free(pkey); + return ret; +} + +int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) { + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate, + ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + if (!ssl_cert_inst(&ctx->cert)) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate, ERR_R_MALLOC_FAILURE); + return 0; + } + + return ssl_set_cert(ctx->cert, x); +} + +static int ssl_set_cert(CERT *c, X509 *x) { + EVP_PKEY *pkey; + int i; + + pkey = X509_get_pubkey(x); + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_set_cert, SSL_R_X509_LIB); + return 0; + } + + i = ssl_cert_type(pkey); + if (i < 0) { + OPENSSL_PUT_ERROR(SSL, ssl_set_cert, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + EVP_PKEY_free(pkey); + return 0; + } + + if (c->pkeys[i].privatekey != NULL) { + EVP_PKEY_copy_parameters(pkey, c->pkeys[i].privatekey); + ERR_clear_error(); + + /* Sanity-check that the private key and the certificate match, unless the + * key is opaque (in case of, say, a smartcard). */ + if (!EVP_PKEY_is_opaque(c->pkeys[i].privatekey) && + !X509_check_private_key(x, c->pkeys[i].privatekey)) { + /* 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(c->pkeys[i].privatekey); + c->pkeys[i].privatekey = NULL; + /* clear error queue */ + ERR_clear_error(); + } + } + + EVP_PKEY_free(pkey); + + if (c->pkeys[i].x509 != NULL) { + X509_free(c->pkeys[i].x509); + } + c->pkeys[i].x509 = X509_up_ref(x); + c->key = &(c->pkeys[i]); + + return 1; +} + +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, SSL_CTX_use_certificate_file, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_file, 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_CTX_use_certificate_file, + SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_file, reason_code); + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + +end: + if (x != NULL) { + X509_free(x); + } + if (in != NULL) { + BIO_free(in); + } + return ret; +} + +int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, int len, const uint8_t *d) { + X509 *x; + int ret; + + x = d2i_X509(NULL, &d, (long)len); + if (x == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_ASN1, ERR_R_ASN1_LIB); + return 0; + } + + ret = SSL_CTX_use_certificate(ctx, x); + X509_free(x); + return ret; +} + +int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa) { + int ret; + EVP_PKEY *pkey; + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey, + ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (!ssl_cert_inst(&ctx->cert)) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey, ERR_R_MALLOC_FAILURE); + return 0; + } + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey, 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_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, SSL_CTX_use_RSAPrivateKey_file, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey_file, 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_CTX_use_RSAPrivateKey_file, + SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey_file, reason_code); + goto end; + } + ret = SSL_CTX_use_RSAPrivateKey(ctx, rsa); + RSA_free(rsa); + +end: + if (in != NULL) { + BIO_free(in); + } + return ret; +} + +int SSL_CTX_use_RSAPrivateKey_ASN1(SSL_CTX *ctx, const uint8_t *d, long len) { + int ret; + const uint8_t *p; + RSA *rsa; + + p = d; + rsa = d2i_RSAPrivateKey(NULL, &p, (long)len); + if (rsa == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_RSAPrivateKey_ASN1, ERR_R_ASN1_LIB); + return 0; + } + + 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, SSL_CTX_use_PrivateKey, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (!ssl_cert_inst(&ctx->cert)) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey, ERR_R_MALLOC_FAILURE); + return 0; + } + + return ssl_set_pkey(ctx->cert, pkey); +} + +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, SSL_CTX_use_PrivateKey_file, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey_file, 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_CTX_use_PrivateKey_file, SSL_R_BAD_SSL_FILETYPE); + goto end; + } + + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey_file, reason_code); + goto end; + } + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); + +end: + if (in != NULL) { + BIO_free(in); + } + return ret; +} + +int SSL_CTX_use_PrivateKey_ASN1(int type, SSL_CTX *ctx, const uint8_t *d, + long len) { + int ret; + const uint8_t *p; + EVP_PKEY *pkey; + + p = d; + pkey = d2i_PrivateKey(type, NULL, &p, (long)len); + if (pkey == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_PrivateKey_ASN1, ERR_R_ASN1_LIB); + return 0; + } + + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); + 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, SSL_CTX_use_certificate_chain_file, ERR_R_BUF_LIB); + goto end; + } + + if (BIO_read_filename(in, file) <= 0) { + OPENSSL_PUT_ERROR(SSL, SSL_CTX_use_certificate_chain_file, 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, SSL_CTX_use_certificate_chain_file, 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; + unsigned long 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: + if (x != NULL) { + X509_free(x); + } + if (in != NULL) { + BIO_free(in); + } + return ret; +} diff --git a/src/ssl/ssl_sess.c b/src/ssl/ssl_sess.c new file mode 100644 index 0000000..c5069d8 --- /dev/null +++ b/src/ssl/ssl_sess.c @@ -0,0 +1,920 @@ +/* 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 <stdio.h> + +#include <openssl/engine.h> +#include <openssl/err.h> +#include <openssl/lhash.h> +#include <openssl/mem.h> +#include <openssl/rand.h> + +#include "ssl_locl.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 void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s); +static void SSL_SESSION_list_add(SSL_CTX *ctx,SSL_SESSION *s); +static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck); + +SSL_SESSION *SSL_magic_pending_session_ptr(void) { + return (SSL_SESSION *)&g_pending_session_magic; +} + +SSL_SESSION *SSL_get_session(const SSL *ssl) +{ + /* aka SSL_get0_session; gets 0 objects, just returns a copy of the pointer */ + return ssl->session; +} + +SSL_SESSION *SSL_get1_session(SSL *ssl) { + /* variant of SSL_get_session: caller really gets something */ + SSL_SESSION *sess; + /* Need to lock this all up rather than just use CRYPTO_add so that + * somebody doesn't free ssl->session between when we check it's + * non-null and when we up the reference count. */ + CRYPTO_w_lock(CRYPTO_LOCK_SSL_SESSION); + sess = ssl->session; + if (sess) { + sess->references++; + } + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_SESSION); + + return sess; +} + +int SSL_SESSION_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func, + CRYPTO_EX_dup *dup_func, + CRYPTO_EX_free *free_func) { + return CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL_SESSION, argl, argp, + new_func, dup_func, free_func); +} + +int SSL_SESSION_set_ex_data(SSL_SESSION *s, int idx, void *arg) { + return CRYPTO_set_ex_data(&s->ex_data, idx, arg); +} + +void *SSL_SESSION_get_ex_data(const SSL_SESSION *s, int idx) { + return CRYPTO_get_ex_data(&s->ex_data, idx); +} + +SSL_SESSION *SSL_SESSION_new(void) { + SSL_SESSION *ss; + + ss = (SSL_SESSION *)OPENSSL_malloc(sizeof(SSL_SESSION)); + if (ss == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_new, ERR_R_MALLOC_FAILURE); + return 0; + } + memset(ss, 0, sizeof(SSL_SESSION)); + + ss->verify_result = 1; /* avoid 0 (= X509_V_OK) just in case */ + ss->references = 1; + ss->timeout = SSL_DEFAULT_SESSION_TIMEOUT; + ss->time = (unsigned long)time(NULL); + CRYPTO_new_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data); + return ss; +} + +const uint8_t *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len) { + if (len) { + *len = s->session_id_length; + } + return s->session_id; +} + +/* Even with SSLv2, we have 16 bytes (128 bits) of session ID space. + * SSLv3/TLSv1 has 32 bytes (256 bits). As such, filling the ID with random + * gunk repeatedly until we have no conflict is going to complete in one + * iteration pretty much "most" of the time (btw: understatement). So, if it + * takes us 10 iterations and we still can't avoid a conflict - well that's a + * reasonable point to call it quits. Either the RAND code is broken or someone + * is trying to open roughly very close to 2^128 (or 2^256) SSL sessions to our + * server. How you might store that many sessions is perhaps a more interesting + * question ... */ +static int def_generate_session_id(const SSL *ssl, uint8_t *id, + unsigned int *id_len) { + static const unsigned kMaxAttempts = 10; + unsigned int retry = 0; + do { + if (!RAND_bytes(id, *id_len)) { + return 0; + } + } while (SSL_has_matching_session_id(ssl, id, *id_len) && + (++retry < kMaxAttempts)); + + if (retry < kMaxAttempts) { + return 1; + } + + /* else - woops a session_id match */ + /* XXX We should also check the external cache -- but the probability of a + * collision is negligible, and we could not prevent the concurrent creation + * of sessions with identical IDs since we currently don't have means to + * atomically check whether a session ID already exists and make a + * reservation for it if it does not (this problem applies to the internal + * cache as well). */ + return 0; +} + +int ssl_get_new_session(SSL *s, int session) { + /* This gets used by clients and servers. */ + + unsigned int tmp; + SSL_SESSION *ss = NULL; + GEN_SESSION_CB cb = def_generate_session_id; + + if (s->mode & SSL_MODE_NO_SESSION_CREATION) { + OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, + SSL_R_SESSION_MAY_NOT_BE_CREATED); + return 0; + } + + ss = SSL_SESSION_new(); + if (ss == NULL) { + return 0; + } + + /* If the context has a default timeout, use it over the default. */ + if (s->initial_ctx->session_timeout != 0) { + ss->timeout = s->initial_ctx->session_timeout; + } + + if (s->session != NULL) { + SSL_SESSION_free(s->session); + s->session = NULL; + } + + if (session) { + if (s->version == SSL3_VERSION || s->version == TLS1_VERSION || + s->version == TLS1_1_VERSION || s->version == TLS1_2_VERSION || + s->version == DTLS1_VERSION || s->version == DTLS1_2_VERSION) { + ss->ssl_version = s->version; + ss->session_id_length = SSL3_SSL_SESSION_ID_LENGTH; + } else { + OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, + SSL_R_UNSUPPORTED_SSL_VERSION); + SSL_SESSION_free(ss); + return 0; + } + + /* If RFC4507 ticket use empty session ID */ + if (s->tlsext_ticket_expected) { + ss->session_id_length = 0; + goto sess_id_done; + } + + /* Choose which callback will set the session ID */ + CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); + if (s->generate_session_id) { + cb = s->generate_session_id; + } else if (s->initial_ctx->generate_session_id) { + cb = s->initial_ctx->generate_session_id; + } + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); + + /* Choose a session ID */ + tmp = ss->session_id_length; + if (!cb(s, ss->session_id, &tmp)) { + /* The callback failed */ + OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, + SSL_R_SSL_SESSION_ID_CALLBACK_FAILED); + SSL_SESSION_free(ss); + return 0; + } + + /* Don't allow the callback to set the session length to zero. nor set it + * higher than it was. */ + if (!tmp || tmp > ss->session_id_length) { + /* The callback set an illegal length */ + OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, + SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH); + SSL_SESSION_free(ss); + return 0; + } + + ss->session_id_length = tmp; + /* Finally, check for a conflict */ + if (SSL_has_matching_session_id(s, ss->session_id, ss->session_id_length)) { + OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, + SSL_R_SSL_SESSION_ID_CONFLICT); + SSL_SESSION_free(ss); + return 0; + } + + sess_id_done: + if (s->tlsext_hostname) { + ss->tlsext_hostname = BUF_strdup(s->tlsext_hostname); + if (ss->tlsext_hostname == NULL) { + OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, ERR_R_INTERNAL_ERROR); + SSL_SESSION_free(ss); + return 0; + } + } + } else { + ss->session_id_length = 0; + } + + if (s->sid_ctx_length > sizeof(ss->sid_ctx)) { + OPENSSL_PUT_ERROR(SSL, ssl_get_new_session, ERR_R_INTERNAL_ERROR); + SSL_SESSION_free(ss); + return 0; + } + + memcpy(ss->sid_ctx, s->sid_ctx, s->sid_ctx_length); + ss->sid_ctx_length = s->sid_ctx_length; + s->session = ss; + ss->ssl_version = s->version; + ss->verify_result = X509_V_OK; + + return 1; +} + +/* ssl_get_prev attempts to find an SSL_SESSION to be used to resume this + * connection. It is only called by servers. + * + * ctx: contains the early callback context, which is the result of a + * shallow parse of the ClientHello. + * + * Returns: + * -1: error + * 0: a session may have been found. + * + * Side effects: + * - If a session is found then s->session is pointed at it (after freeing an + * existing session if need be) and s->verify_result is set from the session. + * - Both for new and resumed sessions, s->tlsext_ticket_expected is set to 1 + * if the server should issue a new session ticket (to 0 otherwise). */ +int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx) { + /* This is used only by servers. */ + SSL_SESSION *ret = NULL; + int fatal = 0; + int try_session_cache = 1; + int r; + + if (ctx->session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) { + goto err; + } + + if (ctx->session_id_len == 0) { + try_session_cache = 0; + } + + r = tls1_process_ticket(s, ctx, &ret); /* sets s->tlsext_ticket_expected */ + switch (r) { + case -1: /* Error during processing */ + fatal = 1; + goto err; + + case 0: /* No ticket found */ + case 1: /* Zero length ticket found */ + break; /* Ok to carry on processing session id. */ + + case 2: /* Ticket found but not decrypted. */ + case 3: /* Ticket decrypted, *ret has been set. */ + try_session_cache = 0; + break; + + default: + abort(); + } + + if (try_session_cache && ret == NULL && + !(s->initial_ctx->session_cache_mode & + SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) { + SSL_SESSION data; + data.ssl_version = s->version; + data.session_id_length = ctx->session_id_len; + if (ctx->session_id_len == 0) { + return 0; + } + memcpy(data.session_id, ctx->session_id, ctx->session_id_len); + CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); + ret = lh_SSL_SESSION_retrieve(s->initial_ctx->sessions, &data); + if (ret != NULL) { + /* don't allow other threads to steal it: */ + CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION); + } + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); + if (ret == NULL) { + s->initial_ctx->stats.sess_miss++; + } + } + + if (try_session_cache && ret == NULL && + s->initial_ctx->get_session_cb != NULL) { + int copy = 1; + + ret = s->initial_ctx->get_session_cb(s, (uint8_t *)ctx->session_id, + ctx->session_id_len, ©); + if (ret != NULL) { + if (ret == SSL_magic_pending_session_ptr()) { + /* This is a magic value which indicates that the callback needs to + * unwind the stack and figure out the session asynchronously. */ + return PENDING_SESSION; + } + s->initial_ctx->stats.sess_cb_hit++; + + /* 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) { + CRYPTO_add(&ret->references, 1, CRYPTO_LOCK_SSL_SESSION); + } + + /* Add the externally cached session to the internal cache as well if and + * only if we are supposed to. */ + if (!(s->initial_ctx->session_cache_mode & + SSL_SESS_CACHE_NO_INTERNAL_STORE)) { + /* The following should not return 1, otherwise, things are very + * strange */ + SSL_CTX_add_session(s->initial_ctx, ret); + } + } + } + + if (ret == NULL) { + goto err; + } + + /* Now ret is non-NULL and we own one of its reference counts. */ + + if (ret->sid_ctx_length != s->sid_ctx_length || + memcmp(ret->sid_ctx, s->sid_ctx, ret->sid_ctx_length)) { + /* We have the session requested by the client, but we don't want to use it + * in this context. */ + goto err; /* treat like cache miss */ + } + + if ((s->verify_mode & SSL_VERIFY_PEER) && s->sid_ctx_length == 0) { + /* We can't be sure if this session is being used out of context, which is + * especially important for SSL_VERIFY_PEER. The application should have + * used SSL[_CTX]_set_session_id_context. + * + * For this error case, we generate an error instead of treating the event + * like a cache miss (otherwise it would be easy for applications to + * effectively disable the session cache by accident without anyone + * noticing). */ + OPENSSL_PUT_ERROR(SSL, ssl_get_prev_session, + SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED); + fatal = 1; + goto err; + } + + if (ret->timeout < (long)(time(NULL) - ret->time)) { + /* timeout */ + s->initial_ctx->stats.sess_timeout++; + if (try_session_cache) { + /* session was from the cache, so remove it */ + SSL_CTX_remove_session(s->initial_ctx, ret); + } + goto err; + } + + s->initial_ctx->stats.sess_hit++; + + if (s->session != NULL) { + SSL_SESSION_free(s->session); + } + s->session = ret; + s->verify_result = s->session->verify_result; + return 1; + +err: + if (ret != NULL) { + SSL_SESSION_free(ret); + if (!try_session_cache) { + /* The session was from a ticket, so we should + * issue a ticket for the new session */ + s->tlsext_ticket_expected = 1; + } + } + if (fatal) { + return -1; + } + return 0; +} + +int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c) { + int ret = 0; + SSL_SESSION *s; + + /* add just 1 reference count for the SSL_CTX's session cache even though it + * has two ways of access: each session is in a doubly linked list and an + * lhash */ + CRYPTO_add(&c->references, 1, CRYPTO_LOCK_SSL_SESSION); + /* if session c is in already in cache, we take back the increment later */ + + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + if (!lh_SSL_SESSION_insert(ctx->sessions, &s, c)) { + return 0; + } + + /* s != NULL iff we already had a session with the given PID. In this case, s + * == c should hold (then we did not really modify ctx->sessions), or we're + * in trouble. */ + if (s != NULL && s != c) { + /* We *are* in trouble ... */ + SSL_SESSION_list_remove(ctx, s); + SSL_SESSION_free(s); + /* ... so pretend the other session did not exist in cache (we cannot + * handle two SSL_SESSION structures with identical session ID in the same + * cache, which could happen e.g. when two threads concurrently obtain the + * same session from an external cache) */ + s = NULL; + } + + /* Put at the head of the queue unless it is already in the cache */ + if (s == NULL) { + SSL_SESSION_list_add(ctx, c); + } + + if (s != NULL) { + /* existing cache entry -- decrement previously incremented reference count + * because it already takes into account the cache */ + SSL_SESSION_free(s); /* s == c */ + ret = 0; + } else { + /* new cache entry -- remove old ones if cache has become too large */ + ret = 1; + + 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; + } + ctx->stats.sess_cache_full++; + } + } + } + + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + return ret; +} + +int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *c) { + return remove_session_lock(ctx, c, 1); +} + +static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lock) { + SSL_SESSION *r; + int ret = 0; + + if (c != NULL && c->session_id_length != 0) { + if (lock) { + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + } + r = lh_SSL_SESSION_retrieve(ctx->sessions, c); + if (r == c) { + ret = 1; + r = lh_SSL_SESSION_delete(ctx->sessions, c); + SSL_SESSION_list_remove(ctx, c); + } + + if (lock) { + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + } + + if (ret) { + r->not_resumable = 1; + if (ctx->remove_session_cb != NULL) { + ctx->remove_session_cb(ctx, r); + } + SSL_SESSION_free(r); + } + } + + return ret; +} + +void SSL_SESSION_free(SSL_SESSION *ss) { + int i; + + if (ss == NULL) { + return; + } + + i = CRYPTO_add(&ss->references, -1, CRYPTO_LOCK_SSL_SESSION); + if (i > 0) { + return; + } + + CRYPTO_free_ex_data(CRYPTO_EX_INDEX_SSL_SESSION, ss, &ss->ex_data); + + OPENSSL_cleanse(ss->master_key, sizeof ss->master_key); + OPENSSL_cleanse(ss->session_id, sizeof ss->session_id); + if (ss->sess_cert != NULL) { + ssl_sess_cert_free(ss->sess_cert); + } + if (ss->peer != NULL) { + X509_free(ss->peer); + } + if (ss->tlsext_hostname != NULL) { + OPENSSL_free(ss->tlsext_hostname); + } + if (ss->tlsext_tick != NULL) { + OPENSSL_free(ss->tlsext_tick); + } + if (ss->tlsext_signed_cert_timestamp_list != NULL) { + OPENSSL_free(ss->tlsext_signed_cert_timestamp_list); + } + if (ss->ocsp_response != NULL) { + OPENSSL_free(ss->ocsp_response); + } + if (ss->psk_identity != NULL) { + OPENSSL_free(ss->psk_identity); + } + OPENSSL_cleanse(ss, sizeof(*ss)); + OPENSSL_free(ss); +} + +int SSL_set_session(SSL *s, SSL_SESSION *session) { + if (s->session == session) { + return 1; + } + + if (s->session != NULL) { + SSL_SESSION_free(s->session); + } + s->session = session; + if (session != NULL) { + CRYPTO_add(&session->references, 1, CRYPTO_LOCK_SSL_SESSION); + s->verify_result = session->verify_result; + } + + return 1; +} + +long SSL_SESSION_set_timeout(SSL_SESSION *s, long t) { + if (s == NULL) { + return 0; + } + + s->timeout = t; + return 1; +} + +long SSL_SESSION_get_timeout(const SSL_SESSION *s) { + if (s == NULL) { + return 0; + } + + return s->timeout; +} + +long SSL_SESSION_get_time(const SSL_SESSION *s) { + if (s == NULL) { + return 0; + } + + return s->time; +} + +long SSL_SESSION_set_time(SSL_SESSION *s, long t) { + if (s == NULL) { + return 0; + } + + s->time = t; + return t; +} + +X509 *SSL_SESSION_get0_peer(SSL_SESSION *s) { return s->peer; } + +int SSL_SESSION_set1_id_context(SSL_SESSION *s, const uint8_t *sid_ctx, + unsigned int sid_ctx_len) { + if (sid_ctx_len > SSL_MAX_SID_CTX_LENGTH) { + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_set1_id_context, + SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG); + return 0; + } + + s->sid_ctx_length = sid_ctx_len; + memcpy(s->sid_ctx, sid_ctx, sid_ctx_len); + + return 1; +} + +long SSL_CTX_set_timeout(SSL_CTX *s, long t) { + long l; + if (s == NULL) { + return 0; + } + + l = s->session_timeout; + s->session_timeout = t; + return l; +} + +long SSL_CTX_get_timeout(const SSL_CTX *s) { + if (s == NULL) { + return 0; + } + + return s->session_timeout; +} + +typedef struct timeout_param_st { + SSL_CTX *ctx; + long time; + LHASH_OF(SSL_SESSION) * cache; +} TIMEOUT_PARAM; + +static void timeout_doall_arg(SSL_SESSION *sess, void *void_param) { + TIMEOUT_PARAM *param = void_param; + + if (param->time == 0 || + param->time > (sess->time + sess->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, sess); + SSL_SESSION_list_remove(param->ctx, sess); + sess->not_resumable = 1; + if (param->ctx->remove_session_cb != NULL) { + param->ctx->remove_session_cb(param->ctx, sess); + } + SSL_SESSION_free(sess); + } +} + +void SSL_CTX_flush_sessions(SSL_CTX *s, long t) { + TIMEOUT_PARAM tp; + + tp.ctx = s; + tp.cache = s->sessions; + if (tp.cache == NULL) { + return; + } + tp.time = t; + CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + lh_SSL_SESSION_doall_arg(tp.cache, timeout_doall_arg, &tp); + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); +} + +int ssl_clear_bad_session(SSL *s) { + if (s->session != NULL && !(s->shutdown & SSL_SENT_SHUTDOWN) && + !SSL_in_init(s)) { + SSL_CTX_remove_session(s->ctx, s->session); + return 1; + } + + return 0; +} + +/* locked by SSL_CTX in the calling function */ +static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s) { + if (s->next == NULL || s->prev == NULL) { + return; + } + + if (s->next == (SSL_SESSION *)&ctx->session_cache_tail) { + /* last element in list */ + if (s->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 = s->prev; + s->prev->next = (SSL_SESSION *)&(ctx->session_cache_tail); + } + } else { + if (s->prev == (SSL_SESSION *)&ctx->session_cache_head) { + /* first element in list */ + ctx->session_cache_head = s->next; + s->next->prev = (SSL_SESSION *)&(ctx->session_cache_head); + } else { /* middle of list */ + s->next->prev = s->prev; + s->prev->next = s->next; + } + } + s->prev = s->next = NULL; +} + +static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s) { + if (s->next != NULL && s->prev != NULL) { + SSL_SESSION_list_remove(ctx, s); + } + + if (ctx->session_cache_head == NULL) { + ctx->session_cache_head = s; + ctx->session_cache_tail = s; + s->prev = (SSL_SESSION *)&(ctx->session_cache_head); + s->next = (SSL_SESSION *)&(ctx->session_cache_tail); + } else { + s->next = ctx->session_cache_head; + s->next->prev = s; + s->prev = (SSL_SESSION *)&(ctx->session_cache_head); + ctx->session_cache_head = s; + } +} + +void SSL_CTX_sess_set_new_cb(SSL_CTX *ctx, + int (*cb)(struct ssl_st *ssl, SSL_SESSION *sess)) { + ctx->new_session_cb = cb; +} + +int (*SSL_CTX_sess_get_new_cb(SSL_CTX *ctx))(SSL *ssl, SSL_SESSION *sess) { + return ctx->new_session_cb; +} + +void SSL_CTX_sess_set_remove_cb(SSL_CTX *ctx, + void (*cb)(SSL_CTX *ctx, SSL_SESSION *sess)) { + ctx->remove_session_cb = cb; +} + +void (*SSL_CTX_sess_get_remove_cb(SSL_CTX *ctx))(SSL_CTX *ctx, + SSL_SESSION *sess) { + return ctx->remove_session_cb; +} + +void SSL_CTX_sess_set_get_cb(SSL_CTX *ctx, + SSL_SESSION *(*cb)(struct ssl_st *ssl, + uint8_t *data, int len, + int *copy)) { + ctx->get_session_cb = cb; +} + +SSL_SESSION *(*SSL_CTX_sess_get_get_cb(SSL_CTX *ctx))(SSL *ssl, uint8_t *data, + int len, int *copy) { + return ctx->get_session_cb; +} + +void SSL_CTX_set_info_callback(SSL_CTX *ctx, + void (*cb)(const SSL *ssl, int type, int val)) { + ctx->info_callback = cb; +} + +void (*SSL_CTX_get_info_callback(SSL_CTX *ctx))(const SSL *ssl, int type, + int val) { + return ctx->info_callback; +} + +void SSL_CTX_set_client_cert_cb(SSL_CTX *ctx, int (*cb)(SSL *ssl, X509 **x509, + EVP_PKEY **pkey)) { + ctx->client_cert_cb = cb; +} + +int (*SSL_CTX_get_client_cert_cb(SSL_CTX *ctx))(SSL *ssl, X509 **x509, + EVP_PKEY **pkey) { + return ctx->client_cert_cb; +} + +void SSL_CTX_set_cookie_generate_cb(SSL_CTX *ctx, + int (*cb)(SSL *ssl, uint8_t *cookie, + size_t *cookie_len)) { + ctx->app_gen_cookie_cb = cb; +} + +void SSL_CTX_set_cookie_verify_cb(SSL_CTX *ctx, + int (*cb)(SSL *ssl, const uint8_t *cookie, + size_t cookie_len)) { + ctx->app_verify_cookie_cb = cb; +} + +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; +} + +IMPLEMENT_PEM_rw(SSL_SESSION, SSL_SESSION, PEM_STRING_SSL_SESSION, SSL_SESSION) diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c new file mode 100644 index 0000000..450ed7c --- /dev/null +++ b/src/ssl/ssl_stat.c @@ -0,0 +1,1018 @@ +/* ssl/ssl_stat.c */ +/* 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 <stdio.h> +#include "ssl_locl.h" + +const char *SSL_state_string_long(const SSL *s) { + const char *str; + + switch (s->state) { + case SSL_ST_ACCEPT: + str = "before accept initialization"; + break; + + case SSL_ST_CONNECT: + str = "before connect initialization"; + break; + + case SSL_ST_OK: + str = "SSL negotiation finished successfully"; + break; + + case SSL_ST_RENEGOTIATE: + str = "SSL renegotiate ciphers"; + break; + + case SSL_ST_BEFORE | SSL_ST_CONNECT: + str = "before/connect initialization"; + break; + + case SSL_ST_BEFORE | SSL_ST_ACCEPT: + str = "before/accept initialization"; + break; + + /* SSLv3 additions */ + case SSL3_ST_CW_CLNT_HELLO_A: + str = "SSLv3 write client hello A"; + break; + + case SSL3_ST_CW_CLNT_HELLO_B: + str = "SSLv3 write client hello B"; + break; + + case SSL3_ST_CR_SRVR_HELLO_A: + str = "SSLv3 read server hello A"; + break; + + case SSL3_ST_CR_SRVR_HELLO_B: + str = "SSLv3 read server hello B"; + break; + + case SSL3_ST_CR_CERT_A: + str = "SSLv3 read server certificate A"; + break; + + case SSL3_ST_CR_CERT_B: + str = "SSLv3 read server certificate B"; + break; + + case SSL3_ST_CR_KEY_EXCH_A: + str = "SSLv3 read server key exchange A"; + break; + + case SSL3_ST_CR_KEY_EXCH_B: + str = "SSLv3 read server key exchange B"; + break; + + case SSL3_ST_CR_CERT_REQ_A: + str = "SSLv3 read server certificate request A"; + break; + + case SSL3_ST_CR_CERT_REQ_B: + str = "SSLv3 read server certificate request B"; + break; + + case SSL3_ST_CR_SESSION_TICKET_A: + str = "SSLv3 read server session ticket A"; + break; + + case SSL3_ST_CR_SESSION_TICKET_B: + str = "SSLv3 read server session ticket B"; + break; + + case SSL3_ST_CR_SRVR_DONE_A: + str = "SSLv3 read server done A"; + break; + + case SSL3_ST_CR_SRVR_DONE_B: + str = "SSLv3 read server done B"; + break; + + case SSL3_ST_CW_CERT_A: + str = "SSLv3 write client certificate A"; + break; + + case SSL3_ST_CW_CERT_B: + str = "SSLv3 write client certificate B"; + break; + + case SSL3_ST_CW_CERT_C: + str = "SSLv3 write client certificate C"; + break; + + case SSL3_ST_CW_CERT_D: + str = "SSLv3 write client certificate D"; + break; + + case SSL3_ST_CW_KEY_EXCH_A: + str = "SSLv3 write client key exchange A"; + break; + + case SSL3_ST_CW_KEY_EXCH_B: + str = "SSLv3 write client key exchange B"; + break; + + case SSL3_ST_CW_CERT_VRFY_A: + str = "SSLv3 write certificate verify A"; + break; + + case SSL3_ST_CW_CERT_VRFY_B: + str = "SSLv3 write certificate verify B"; + break; + + case SSL3_ST_CW_CHANGE_A: + case SSL3_ST_SW_CHANGE_A: + str = "SSLv3 write change cipher spec A"; + break; + + case SSL3_ST_CW_CHANGE_B: + case SSL3_ST_SW_CHANGE_B: + str = "SSLv3 write change cipher spec B"; + break; + + case SSL3_ST_CW_FINISHED_A: + case SSL3_ST_SW_FINISHED_A: + str = "SSLv3 write finished A"; + break; + + case SSL3_ST_CW_FINISHED_B: + case SSL3_ST_SW_FINISHED_B: + str = "SSLv3 write finished B"; + break; + + case SSL3_ST_CR_CHANGE: + case SSL3_ST_SR_CHANGE: + str = "SSLv3 read change cipher spec"; + break; + + case SSL3_ST_CR_FINISHED_A: + case SSL3_ST_SR_FINISHED_A: + str = "SSLv3 read finished A"; + break; + + case SSL3_ST_CR_FINISHED_B: + case SSL3_ST_SR_FINISHED_B: + str = "SSLv3 read finished B"; + break; + + case SSL3_ST_CW_FLUSH: + case SSL3_ST_SW_FLUSH: + str = "SSLv3 flush data"; + break; + + case SSL3_ST_SR_CLNT_HELLO_A: + str = "SSLv3 read client hello A"; + break; + + case SSL3_ST_SR_CLNT_HELLO_B: + str = "SSLv3 read client hello B"; + break; + + case SSL3_ST_SR_CLNT_HELLO_C: + str = "SSLv3 read client hello C"; + break; + + case SSL3_ST_SR_CLNT_HELLO_D: + str = "SSLv3 read client hello D"; + break; + + case SSL3_ST_SW_HELLO_REQ_A: + str = "SSLv3 write hello request A"; + break; + + case SSL3_ST_SW_HELLO_REQ_B: + str = "SSLv3 write hello request B"; + break; + + case SSL3_ST_SW_HELLO_REQ_C: + str = "SSLv3 write hello request C"; + break; + + case SSL3_ST_SW_SRVR_HELLO_A: + str = "SSLv3 write server hello A"; + break; + + case SSL3_ST_SW_SRVR_HELLO_B: + str = "SSLv3 write server hello B"; + break; + + case SSL3_ST_SW_CERT_A: + str = "SSLv3 write certificate A"; + break; + + case SSL3_ST_SW_CERT_B: + str = "SSLv3 write certificate B"; + break; + + case SSL3_ST_SW_KEY_EXCH_A: + str = "SSLv3 write key exchange A"; + break; + + case SSL3_ST_SW_KEY_EXCH_B: + str = "SSLv3 write key exchange B"; + break; + + case SSL3_ST_SW_CERT_REQ_A: + str = "SSLv3 write certificate request A"; + break; + + case SSL3_ST_SW_CERT_REQ_B: + str = "SSLv3 write certificate request B"; + break; + + case SSL3_ST_SW_SESSION_TICKET_A: + str = "SSLv3 write session ticket A"; + break; + + case SSL3_ST_SW_SESSION_TICKET_B: + str = "SSLv3 write session ticket B"; + break; + + case SSL3_ST_SW_SRVR_DONE_A: + str = "SSLv3 write server done A"; + break; + + case SSL3_ST_SW_SRVR_DONE_B: + str = "SSLv3 write server done B"; + break; + + case SSL3_ST_SR_CERT_A: + str = "SSLv3 read client certificate A"; + break; + + case SSL3_ST_SR_CERT_B: + str = "SSLv3 read client certificate B"; + break; + + case SSL3_ST_SR_KEY_EXCH_A: + str = "SSLv3 read client key exchange A"; + break; + + case SSL3_ST_SR_KEY_EXCH_B: + str = "SSLv3 read client key exchange B"; + break; + + case SSL3_ST_SR_CERT_VRFY_A: + str = "SSLv3 read certificate verify A"; + break; + + case SSL3_ST_SR_CERT_VRFY_B: + str = "SSLv3 read certificate verify B"; + break; + + /* SSLv2/v3 compatibility states */ + /* client */ + case SSL23_ST_CW_CLNT_HELLO_A: + str = "SSLv2/v3 write client hello A"; + break; + + case SSL23_ST_CW_CLNT_HELLO_B: + str = "SSLv2/v3 write client hello B"; + break; + + case SSL23_ST_CR_SRVR_HELLO_A: + str = "SSLv2/v3 read server hello A"; + break; + + case SSL23_ST_CR_SRVR_HELLO_B: + str = "SSLv2/v3 read server hello B"; + break; + + /* server */ + case SSL23_ST_SR_CLNT_HELLO: + str = "SSLv2/v3 read client hello"; + break; + + case SSL23_ST_SR_V2_CLNT_HELLO: + str = "SSLv2/v3 read v2 client hello"; + break; + + case SSL23_ST_SR_SWITCH_VERSION: + str = "SSLv2/v3 switch version"; + break; + + /* DTLS */ + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: + str = "DTLS1 read hello verify request A"; + break; + + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B: + str = "DTLS1 read hello verify request B"; + break; + + case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A: + str = "DTLS1 write hello verify request A"; + break; + + case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B: + str = "DTLS1 write hello verify request B"; + break; + + default: + str = "unknown state"; + break; + } + + return str; +} + +const char *SSL_rstate_string_long(const SSL *s) { + const char *str; + + switch (s->rstate) { + case SSL_ST_READ_HEADER: + str = "read header"; + break; + + case SSL_ST_READ_BODY: + str = "read body"; + break; + + case SSL_ST_READ_DONE: + str = "read done"; + break; + + default: + str = "unknown"; + break; + } + + return str; +} + +const char *SSL_state_string(const SSL *s) { + const char *str; + + switch (s->state) { + case SSL_ST_ACCEPT: + str = "AINIT "; + break; + + case SSL_ST_CONNECT: + str = "CINIT "; + break; + + case SSL_ST_OK: + str = "SSLOK "; + break; + + /* SSLv3 additions */ + case SSL3_ST_SW_FLUSH: + case SSL3_ST_CW_FLUSH: + str = "3FLUSH"; + break; + + case SSL3_ST_CW_CLNT_HELLO_A: + str = "3WCH_A"; + break; + + case SSL3_ST_CW_CLNT_HELLO_B: + str = "3WCH_B"; + break; + + case SSL3_ST_CR_SRVR_HELLO_A: + str = "3RSH_A"; + break; + + case SSL3_ST_CR_SRVR_HELLO_B: + str = "3RSH_B"; + break; + + case SSL3_ST_CR_CERT_A: + str = "3RSC_A"; + break; + + case SSL3_ST_CR_CERT_B: + str = "3RSC_B"; + break; + + case SSL3_ST_CR_KEY_EXCH_A: + str = "3RSKEA"; + break; + + case SSL3_ST_CR_KEY_EXCH_B: + str = "3RSKEB"; + break; + + case SSL3_ST_CR_CERT_REQ_A: + str = "3RCR_A"; + break; + + case SSL3_ST_CR_CERT_REQ_B: + str = "3RCR_B"; + break; + + case SSL3_ST_CR_SRVR_DONE_A: + str = "3RSD_A"; + break; + + case SSL3_ST_CR_SRVR_DONE_B: + str = "3RSD_B"; + break; + + case SSL3_ST_CW_CERT_A: + str = "3WCC_A"; + break; + + case SSL3_ST_CW_CERT_B: + str = "3WCC_B"; + break; + + case SSL3_ST_CW_CERT_C: + str = "3WCC_C"; + break; + + case SSL3_ST_CW_CERT_D: + str = "3WCC_D"; + break; + + case SSL3_ST_CW_KEY_EXCH_A: + str = "3WCKEA"; + break; + + case SSL3_ST_CW_KEY_EXCH_B: + str = "3WCKEB"; + break; + + case SSL3_ST_CW_CERT_VRFY_A: + str = "3WCV_A"; + break; + + case SSL3_ST_CW_CERT_VRFY_B: + str = "3WCV_B"; + break; + + case SSL3_ST_SW_CHANGE_A: + case SSL3_ST_CW_CHANGE_A: + str = "3WCCSA"; + break; + + case SSL3_ST_SW_CHANGE_B: + case SSL3_ST_CW_CHANGE_B: + str = "3WCCSB"; + break; + + case SSL3_ST_SW_FINISHED_A: + case SSL3_ST_CW_FINISHED_A: + str = "3WFINA"; + break; + + case SSL3_ST_SW_FINISHED_B: + case SSL3_ST_CW_FINISHED_B: + str = "3WFINB"; + break; + + case SSL3_ST_CR_CHANGE: + case SSL3_ST_SR_CHANGE: + str = "3RCCS_"; + break; + + case SSL3_ST_SR_FINISHED_A: + case SSL3_ST_CR_FINISHED_A: + str = "3RFINA"; + break; + + case SSL3_ST_SR_FINISHED_B: + case SSL3_ST_CR_FINISHED_B: + str = "3RFINB"; + break; + + case SSL3_ST_SW_HELLO_REQ_A: + str = "3WHR_A"; + break; + + case SSL3_ST_SW_HELLO_REQ_B: + str = "3WHR_B"; + break; + + case SSL3_ST_SW_HELLO_REQ_C: + str = "3WHR_C"; + break; + + case SSL3_ST_SR_CLNT_HELLO_A: + str = "3RCH_A"; + break; + + case SSL3_ST_SR_CLNT_HELLO_B: + str = "3RCH_B"; + break; + + case SSL3_ST_SR_CLNT_HELLO_C: + str = "3RCH_C"; + break; + + case SSL3_ST_SR_CLNT_HELLO_D: + str = "3RCH_D"; + break; + + case SSL3_ST_SW_SRVR_HELLO_A: + str = "3WSH_A"; + break; + + case SSL3_ST_SW_SRVR_HELLO_B: + str = "3WSH_B"; + break; + + case SSL3_ST_SW_CERT_A: + str = "3WSC_A"; + break; + + case SSL3_ST_SW_CERT_B: + str = "3WSC_B"; + break; + + case SSL3_ST_SW_KEY_EXCH_A: + str = "3WSKEA"; + break; + + case SSL3_ST_SW_KEY_EXCH_B: + str = "3WSKEB"; + break; + + case SSL3_ST_SW_CERT_REQ_A: + str = "3WCR_A"; + break; + + case SSL3_ST_SW_CERT_REQ_B: + str = "3WCR_B"; + break; + + case SSL3_ST_SW_SRVR_DONE_A: + str = "3WSD_A"; + break; + + case SSL3_ST_SW_SRVR_DONE_B: + str = "3WSD_B"; + break; + + case SSL3_ST_SR_CERT_A: + str = "3RCC_A"; + break; + + case SSL3_ST_SR_CERT_B: + str = "3RCC_B"; + break; + + case SSL3_ST_SR_KEY_EXCH_A: + str = "3RCKEA"; + break; + + case SSL3_ST_SR_KEY_EXCH_B: + str = "3RCKEB"; + break; + + case SSL3_ST_SR_CERT_VRFY_A: + str = "3RCV_A"; + break; + + case SSL3_ST_SR_CERT_VRFY_B: + str = "3RCV_B"; + break; + + /* SSLv2/v3 compatibility states */ + /* client */ + case SSL23_ST_CW_CLNT_HELLO_A: + str = "23WCHA"; + break; + + case SSL23_ST_CW_CLNT_HELLO_B: + str = "23WCHB"; + break; + + case SSL23_ST_CR_SRVR_HELLO_A: + str = "23RSHA"; + break; + + case SSL23_ST_CR_SRVR_HELLO_B: + str = "23RSHA"; + break; + + /* server */ + case SSL23_ST_SR_CLNT_HELLO: + str = "23RCH_"; + break; + + case SSL23_ST_SR_V2_CLNT_HELLO: + str = "23R2CH"; + break; + + case SSL23_ST_SR_SWITCH_VERSION: + str = "23RSW_"; + break; + + /* DTLS */ + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A: + str = "DRCHVA"; + break; + + case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_B: + str = "DRCHVB"; + break; + + case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A: + str = "DWCHVA"; + break; + + case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B: + str = "DWCHVB"; + break; + + default: + str = "UNKWN "; + break; + } + + return str; +} + +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) { + value >>= 8; + if (value == SSL3_AL_WARNING) { + return "W"; + } else if (value == SSL3_AL_FATAL) { + return "F"; + } + + return "U"; +} + +const char *SSL_alert_desc_string(int value) { + const char *str; + + switch (value & 0xff) { + case SSL3_AD_CLOSE_NOTIFY: + str = "CN"; + break; + + case SSL3_AD_UNEXPECTED_MESSAGE: + str = "UM"; + break; + + case SSL3_AD_BAD_RECORD_MAC: + str = "BM"; + break; + + case SSL3_AD_DECOMPRESSION_FAILURE: + str = "DF"; + break; + + case SSL3_AD_HANDSHAKE_FAILURE: + str = "HF"; + break; + + case SSL3_AD_NO_CERTIFICATE: + str = "NC"; + break; + + case SSL3_AD_BAD_CERTIFICATE: + str = "BC"; + break; + + case SSL3_AD_UNSUPPORTED_CERTIFICATE: + str = "UC"; + break; + + case SSL3_AD_CERTIFICATE_REVOKED: + str = "CR"; + break; + + case SSL3_AD_CERTIFICATE_EXPIRED: + str = "CE"; + break; + + case SSL3_AD_CERTIFICATE_UNKNOWN: + str = "CU"; + break; + + case SSL3_AD_ILLEGAL_PARAMETER: + str = "IP"; + break; + + case TLS1_AD_DECRYPTION_FAILED: + str = "DC"; + break; + + case TLS1_AD_RECORD_OVERFLOW: + str = "RO"; + break; + + case TLS1_AD_UNKNOWN_CA: + str = "CA"; + break; + + case TLS1_AD_ACCESS_DENIED: + str = "AD"; + break; + + case TLS1_AD_DECODE_ERROR: + str = "DE"; + break; + + case TLS1_AD_DECRYPT_ERROR: + str = "CY"; + break; + + case TLS1_AD_EXPORT_RESTRICTION: + str = "ER"; + break; + + case TLS1_AD_PROTOCOL_VERSION: + str = "PV"; + break; + + case TLS1_AD_INSUFFICIENT_SECURITY: + str = "IS"; + break; + + case TLS1_AD_INTERNAL_ERROR: + str = "IE"; + break; + + case TLS1_AD_USER_CANCELLED: + str = "US"; + break; + + case TLS1_AD_NO_RENEGOTIATION: + str = "NR"; + break; + + case TLS1_AD_UNSUPPORTED_EXTENSION: + str = "UE"; + break; + + case TLS1_AD_CERTIFICATE_UNOBTAINABLE: + str = "CO"; + break; + + case TLS1_AD_UNRECOGNIZED_NAME: + str = "UN"; + break; + + case TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + str = "BR"; + break; + + case TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: + str = "BH"; + break; + + case TLS1_AD_UNKNOWN_PSK_IDENTITY: + str = "UP"; + break; + + default: + str = "UK"; + break; + } + + return str; +} + +const char *SSL_alert_desc_string_long(int value) { + const char *str; + + switch (value & 0xff) { + case SSL3_AD_CLOSE_NOTIFY: + str = "close notify"; + break; + + case SSL3_AD_UNEXPECTED_MESSAGE: + str = "unexpected_message"; + break; + + case SSL3_AD_BAD_RECORD_MAC: + str = "bad record mac"; + break; + + case SSL3_AD_DECOMPRESSION_FAILURE: + str = "decompression failure"; + break; + + case SSL3_AD_HANDSHAKE_FAILURE: + str = "handshake failure"; + break; + + case SSL3_AD_NO_CERTIFICATE: + str = "no certificate"; + break; + + case SSL3_AD_BAD_CERTIFICATE: + str = "bad certificate"; + break; + + case SSL3_AD_UNSUPPORTED_CERTIFICATE: + str = "unsupported certificate"; + break; + + case SSL3_AD_CERTIFICATE_REVOKED: + str = "certificate revoked"; + break; + + case SSL3_AD_CERTIFICATE_EXPIRED: + str = "certificate expired"; + break; + + case SSL3_AD_CERTIFICATE_UNKNOWN: + str = "certificate unknown"; + break; + + case SSL3_AD_ILLEGAL_PARAMETER: + str = "illegal parameter"; + break; + + case TLS1_AD_DECRYPTION_FAILED: + str = "decryption failed"; + break; + + case TLS1_AD_RECORD_OVERFLOW: + str = "record overflow"; + break; + + case TLS1_AD_UNKNOWN_CA: + str = "unknown CA"; + break; + + case TLS1_AD_ACCESS_DENIED: + str = "access denied"; + break; + + case TLS1_AD_DECODE_ERROR: + str = "decode error"; + break; + + case TLS1_AD_DECRYPT_ERROR: + str = "decrypt error"; + break; + + case TLS1_AD_EXPORT_RESTRICTION: + str = "export restriction"; + break; + + case TLS1_AD_PROTOCOL_VERSION: + str = "protocol version"; + break; + + case TLS1_AD_INSUFFICIENT_SECURITY: + str = "insufficient security"; + break; + + case TLS1_AD_INTERNAL_ERROR: + str = "internal error"; + break; + + case TLS1_AD_USER_CANCELLED: + str = "user canceled"; + break; + + case TLS1_AD_NO_RENEGOTIATION: + str = "no renegotiation"; + break; + + case TLS1_AD_UNSUPPORTED_EXTENSION: + str = "unsupported extension"; + break; + + case TLS1_AD_CERTIFICATE_UNOBTAINABLE: + str = "certificate unobtainable"; + break; + + case TLS1_AD_UNRECOGNIZED_NAME: + str = "unrecognized name"; + break; + + case TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + str = "bad certificate status response"; + break; + + case TLS1_AD_BAD_CERTIFICATE_HASH_VALUE: + str = "bad certificate hash value"; + break; + + case TLS1_AD_UNKNOWN_PSK_IDENTITY: + str = "unknown PSK identity"; + break; + + default: + str = "unknown"; + break; + } + + return str; +} + +const char *SSL_rstate_string(const SSL *s) { + const char *str; + + switch (s->rstate) { + case SSL_ST_READ_HEADER: + str = "RH"; + break; + + case SSL_ST_READ_BODY: + str = "RB"; + break; + + case SSL_ST_READ_DONE: + str = "RD"; + break; + + default: + str = "unknown"; + break; + } + + return str; +} diff --git a/src/ssl/ssl_test.c b/src/ssl/ssl_test.c new file mode 100644 index 0000000..70291a2 --- /dev/null +++ b/src/ssl/ssl_test.c @@ -0,0 +1,456 @@ +/* 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 <openssl/base64.h> +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/ssl.h> + +typedef struct { + int id; + int in_group_flag; +} EXPECTED_CIPHER; + +typedef struct { + /* The rule string to apply. */ + const char *rule; + /* The list of expected ciphers, in order, terminated with -1. */ + const EXPECTED_CIPHER *expected; +} CIPHER_TEST; + +/* Selecting individual ciphers should work. */ +static const char kRule1[] = + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256"; + +static const EXPECTED_CIPHER kExpected1[] = { + { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 }, + { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 }, + { -1, -1 }, +}; + +/* + reorders selected ciphers to the end, keeping their relative + * order. */ +static const char kRule2[] = + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "+aRSA"; + +static const EXPECTED_CIPHER kExpected2[] = { + { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 }, + { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 }, + { -1, -1 }, +}; + +/* ! banishes ciphers from future selections. */ +static const char kRule3[] = + "!aRSA:" + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256"; + +static const EXPECTED_CIPHER kExpected3[] = { + { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 }, + { -1, -1 }, +}; + +/* Multiple masks can be ANDed in a single rule. */ +static const char kRule4[] = "kRSA+AESGCM+AES128"; + +static const EXPECTED_CIPHER kExpected4[] = { + { TLS1_CK_RSA_WITH_AES_128_GCM_SHA256, 0 }, + { -1, -1 }, +}; + +/* - removes selected ciphers, but preserves their order for future + * selections. Select AES_128_GCM, but order the key exchanges RSA, + * DHE_RSA, ECDHE_RSA. */ +static const char kRule5[] = + "ALL:-kEECDH:-kEDH:-kRSA:-ALL:" + "AESGCM+AES128+aRSA"; + +static const EXPECTED_CIPHER kExpected5[] = { + { 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 }, + { -1, -1 }, +}; + +/* Unknown selectors are no-ops. */ +static const char kRule6[] = + "ECDHE-ECDSA-CHACHA20-POLY1305:" + "ECDHE-RSA-CHACHA20-POLY1305:" + "ECDHE-ECDSA-AES128-GCM-SHA256:" + "ECDHE-RSA-AES128-GCM-SHA256:" + "BOGUS1:-BOGUS2:+BOGUS3:!BOGUS4"; + +static const EXPECTED_CIPHER kExpected6[] = { + { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 }, + { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 }, + { -1, -1 }, +}; + +/* Square brackets specify equi-preference groups. */ +static const char kRule7[] = + "[ECDHE-ECDSA-CHACHA20-POLY1305|ECDHE-ECDSA-AES128-GCM-SHA256]:" + "[ECDHE-RSA-CHACHA20-POLY1305]:" + "ECDHE-RSA-AES128-GCM-SHA256"; + +static const EXPECTED_CIPHER kExpected7[] = { + { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305, 1 }, + { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 }, + { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 }, + { -1, -1 }, +}; + +/* @STRENGTH performs a stable strength-sort of the selected + * ciphers and only the selected ciphers. */ +static const char kRule8[] = + /* To simplify things, banish all but {ECDHE_RSA,RSA} x + * {CHACHA20,AES_256_CBC,AES_128_CBC,RC4} x SHA1. */ + "!kEDH:!AESGCM:!3DES:!SHA256:!MD5:!SHA384:" + /* Order some ciphers backwards by strength. */ + "ALL:-CHACHA20:-AES256:-AES128:-RC4:-ALL:" + /* Select ECDHE ones and sort them by strength. Ties should resolve + * based on the order above. */ + "kEECDH:@STRENGTH:-ALL:" + /* Now bring back everything uses RSA. ECDHE_RSA should be first, + * sorted by strength. Then RSA, backwards by strength. */ + "aRSA"; + +static const EXPECTED_CIPHER kExpected8[] = { + { TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0 }, + { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305, 0 }, + { TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA, 0 }, + { TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0 }, + { SSL3_CK_RSA_RC4_128_SHA, 0 }, + { TLS1_CK_RSA_WITH_AES_128_SHA, 0 }, + { TLS1_CK_RSA_WITH_AES_256_SHA, 0 }, + { -1, -1 }, +}; + +static CIPHER_TEST kCipherTests[] = { + { kRule1, kExpected1 }, + { kRule2, kExpected2 }, + { kRule3, kExpected3 }, + { kRule4, kExpected4 }, + { kRule5, kExpected5 }, + { kRule6, kExpected6 }, + { kRule7, kExpected7 }, + { kRule8, kExpected8 }, + { NULL, NULL }, +}; + +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", + /* 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", + NULL, +}; + +static void print_cipher_preference_list( + struct ssl_cipher_preference_list_st *list) { + size_t i; + int in_group = 0; + for (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 = 1; + } + 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 = 0; + } + } +} + +static int test_cipher_rule(CIPHER_TEST *t) { + int ret = 0; + SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method()); + size_t i; + + if (!SSL_CTX_set_cipher_list(ctx, t->rule)) { + fprintf(stderr, "Error testing cipher rule '%s'\n", t->rule); + BIO_print_errors_fp(stderr); + goto done; + } + + /* Compare the two lists. */ + for (i = 0; i < sk_SSL_CIPHER_num(ctx->cipher_list->ciphers); 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' evaluted to:\n", t->rule); + print_cipher_preference_list(ctx->cipher_list); + goto done; + } + } + + if (t->expected[i].id != -1) { + fprintf(stderr, "Error: cipher rule '%s' evaluted to:\n", t->rule); + print_cipher_preference_list(ctx->cipher_list); + goto done; + } + + ret = 1; +done: + SSL_CTX_free(ctx); + return ret; +} + +static int test_cipher_rules(void) { + size_t i; + for (i = 0; kCipherTests[i].rule != NULL; i++) { + if (!test_cipher_rule(&kCipherTests[i])) { + return 0; + } + } + + for (i = 0; kBadRules[i] != NULL; i++) { + SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method()); + if (SSL_CTX_set_cipher_list(ctx, kBadRules[i])) { + fprintf(stderr, "Cipher rule '%s' unexpectedly succeeded\n", kBadRules[i]); + return 0; + } + ERR_clear_error(); + SSL_CTX_free(ctx); + } + + return 1; +} + +/* kOpenSSLSession is a serialized SSL_SESSION generated from openssl + * s_client -sess_out. */ +static const char kOpenSSLSession[] = + "MIIFpQIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ" + "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" + "i4gv7Y5oliyn"; + +/* 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"; + +static int decode_base64(uint8_t **out, size_t *out_len, const char *in) { + size_t len; + + if (!EVP_DecodedLength(&len, strlen(in))) { + fprintf(stderr, "EVP_DecodedLength failed\n"); + return 0; + } + + *out = OPENSSL_malloc(len); + if (*out == NULL) { + fprintf(stderr, "malloc failed\n"); + return 0; + } + + if (!EVP_DecodeBase64(*out, out_len, len, (const uint8_t *)in, + strlen(in))) { + fprintf(stderr, "EVP_DecodeBase64 failed\n"); + OPENSSL_free(*out); + *out = NULL; + return 0; + } + return 1; +} + +static int test_ssl_session_asn1(const char *input_b64) { + int ret = 0, len; + size_t input_len, encoded_len; + uint8_t *input = NULL, *encoded = NULL; + const uint8_t *cptr; + uint8_t *ptr; + SSL_SESSION *session = NULL; + + /* Decode the input. */ + if (!decode_base64(&input, &input_len, input_b64)) { + goto done; + } + + /* Verify the SSL_SESSION decodes. */ + cptr = input; + session = d2i_SSL_SESSION(NULL, &cptr, input_len); + if (session == NULL || cptr != input + input_len) { + fprintf(stderr, "d2i_SSL_SESSION failed\n"); + goto done; + } + + /* Verify the SSL_SESSION encoding round-trips. */ + if (!SSL_SESSION_to_bytes(session, &encoded, &encoded_len)) { + fprintf(stderr, "SSL_SESSION_to_bytes failed\n"); + goto done; + } + if (encoded_len != input_len || + memcmp(input, encoded, input_len) != 0) { + fprintf(stderr, "SSL_SESSION_to_bytes did not round-trip\n"); + goto done; + } + OPENSSL_free(encoded); + encoded = NULL; + + /* Verify the SSL_SESSION encoding round-trips via the legacy API. */ + len = i2d_SSL_SESSION(session, NULL); + if (len < 0 || (size_t)len != input_len) { + fprintf(stderr, "i2d_SSL_SESSION(NULL) returned invalid length\n"); + goto done; + } + + encoded = OPENSSL_malloc(input_len); + if (encoded == NULL) { + fprintf(stderr, "malloc failed\n"); + goto done; + } + ptr = encoded; + len = i2d_SSL_SESSION(session, &ptr); + if (len < 0 || (size_t)len != input_len) { + fprintf(stderr, "i2d_SSL_SESSION returned invalid length\n"); + goto done; + } + if (ptr != encoded + input_len) { + fprintf(stderr, "i2d_SSL_SESSION did not advance ptr correctly\n"); + goto done; + } + if (memcmp(input, encoded, input_len) != 0) { + fprintf(stderr, "i2d_SSL_SESSION did not round-trip\n"); + goto done; + } + + ret = 1; + + done: + if (!ret) { + BIO_print_errors_fp(stderr); + } + + if (session) { + SSL_SESSION_free(session); + } + if (input) { + OPENSSL_free(input); + } + if (encoded) { + OPENSSL_free(encoded); + } + return ret; +} + +int test_default_version(uint16_t version, const SSL_METHOD *(*method)(void)) { + SSL_CTX *ctx; + int ret; + + ctx = SSL_CTX_new(method()); + if (ctx == NULL) { + return 0; + } + + ret = ctx->min_version == version && ctx->max_version == version; + SSL_CTX_free(ctx); + return ret; +} + +int main(void) { + SSL_library_init(); + + if (!test_cipher_rules() || + !test_ssl_session_asn1(kOpenSSLSession) || + !test_ssl_session_asn1(kCustomSession) || + !test_default_version(0, &TLS_method) || + !test_default_version(SSL3_VERSION, &SSLv3_method) || + !test_default_version(TLS1_VERSION, &TLSv1_method) || + !test_default_version(TLS1_1_VERSION, &TLSv1_1_method) || + !test_default_version(TLS1_2_VERSION, &TLSv1_2_method) || + !test_default_version(0, &DTLS_method) || + !test_default_version(DTLS1_VERSION, &DTLSv1_method) || + !test_default_version(DTLS1_2_VERSION, &DTLSv1_2_method)) { + return 1; + } + + printf("PASS\n"); + return 0; +} diff --git a/src/ssl/ssl_txt.c b/src/ssl/ssl_txt.c new file mode 100644 index 0000000..c950ce8 --- /dev/null +++ b/src/ssl/ssl_txt.c @@ -0,0 +1,211 @@ +/* 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 <inttypes.h> +#include <stdio.h> + +#include <openssl/buf.h> +#include <openssl/err.h> +#include <openssl/mem.h> + +#include "ssl_locl.h" + + +int SSL_SESSION_print_fp(FILE *fp, const SSL_SESSION *x) { + BIO *b; + int ret; + + b = BIO_new(BIO_s_file()); + if (b == NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_SESSION_print_fp, ERR_R_BUF_LIB); + return 0; + } + + BIO_set_fp(b, fp, BIO_NOCLOSE); + ret = SSL_SESSION_print(b, x); + BIO_free(b); + return ret; +} + +int SSL_SESSION_print(BIO *bp, const SSL_SESSION *x) { + unsigned int i; + const char *s; + + if (x == NULL || + BIO_puts(bp, "SSL-Session:\n") <= 0) { + goto err; + } + + if (x->ssl_version == SSL3_VERSION) { + s = "SSLv3"; + } else if (x->ssl_version == TLS1_2_VERSION) { + s = "TLSv1.2"; + } else if (x->ssl_version == TLS1_1_VERSION) { + s = "TLSv1.1"; + } else if (x->ssl_version == TLS1_VERSION) { + s = "TLSv1"; + } else if (x->ssl_version == DTLS1_VERSION) { + s = "DTLSv1"; + } else if (x->ssl_version == DTLS1_2_VERSION) { + s = "DTLSv1.2"; + } else { + s = "unknown"; + } + + if (BIO_printf(bp, " Protocol : %s\n", s) <= 0) { + goto err; + } + + if (BIO_printf(bp, " Cipher : %s\n", + ((x->cipher == NULL) ? "unknown" : x->cipher->name)) <= 0) { + goto err; + } + + if (BIO_puts(bp, " Session-ID: ") <= 0) { + goto err; + } + + for (i = 0; i < x->session_id_length; i++) { + if (BIO_printf(bp, "%02X", x->session_id[i]) <= 0) + goto err; + } + + if (BIO_puts(bp, "\n Session-ID-ctx: ") <= 0) { + goto err; + } + + for (i = 0; i < x->sid_ctx_length; i++) { + if (BIO_printf(bp, "%02X", x->sid_ctx[i]) <= 0) { + goto err; + } + } + + if (BIO_puts(bp, "\n Master-Key: ") <= 0) { + goto err; + } + + for (i = 0; i < (unsigned int)x->master_key_length; i++) { + if (BIO_printf(bp, "%02X", x->master_key[i]) <= 0) { + goto err; + } + } + + if (BIO_puts(bp, "\n PSK identity: ") <= 0 || + BIO_printf(bp, "%s", x->psk_identity ? x->psk_identity : "None") <= 0) { + goto err; + } + + if (x->tlsext_tick_lifetime_hint && + BIO_printf(bp, "\n TLS session ticket lifetime hint: %" PRIu32 + " (seconds)", + x->tlsext_tick_lifetime_hint) <= 0) { + goto err; + } + + if (x->tlsext_tick) { + if (BIO_puts(bp, "\n TLS session ticket:\n") <= 0 || + BIO_hexdump(bp, x->tlsext_tick, x->tlsext_ticklen, 4) <= 0) { + goto err; + } + } + + if (x->time != 0L && BIO_printf(bp, "\n Start Time: %ld", x->time) <= 0) { + goto err; + } + + if (x->timeout != 0L && + BIO_printf(bp, "\n Timeout : %ld (sec)", x->timeout) <= 0) { + goto err; + } + + if (BIO_puts(bp, "\n") <= 0 || + BIO_puts(bp, " Verify return code: ") <= 0 || + BIO_printf(bp, "%ld (%s)\n", x->verify_result, + X509_verify_cert_error_string(x->verify_result)) <= 0) { + goto err; + } + + return 1; + +err: + return 0; +} diff --git a/src/ssl/t1_enc.c b/src/ssl/t1_enc.c new file mode 100644 index 0000000..014bc88 --- /dev/null +++ b/src/ssl/t1_enc.c @@ -0,0 +1,1042 @@ +/* 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 <stdio.h> +#include <assert.h> + +#include <openssl/err.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/md5.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/rand.h> + +#include "ssl_locl.h" + + +/* tls1_P_hash computes the TLS P_<hash> function as described in RFC 5246, + * section 5. It writes |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) { + size_t chunk; + HMAC_CTX ctx, ctx_tmp, ctx_init; + uint8_t A1[EVP_MAX_MD_SIZE]; + unsigned A1_len; + int ret = 0; + + 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) || + (seed1_len && !HMAC_Update(&ctx, seed1, seed1_len)) || + (seed2_len && !HMAC_Update(&ctx, seed2, seed2_len)) || + (seed3_len && !HMAC_Update(&ctx, seed3, seed3_len)) || + !HMAC_Final(&ctx, A1, &A1_len)) { + goto err; + } + + for (;;) { + /* Reinit mac contexts. */ + if (!HMAC_CTX_copy_ex(&ctx, &ctx_init) || + !HMAC_Update(&ctx, A1, A1_len) || + (out_len > chunk && !HMAC_CTX_copy_ex(&ctx_tmp, &ctx)) || + (seed1_len && !HMAC_Update(&ctx, seed1, seed1_len)) || + (seed2_len && !HMAC_Update(&ctx, seed2, seed2_len)) || + (seed3_len && !HMAC_Update(&ctx, seed3, seed3_len))) { + goto err; + } + + if (out_len > chunk) { + unsigned len; + if (!HMAC_Final(&ctx, out, &len)) { + goto err; + } + assert(len == chunk); + out += len; + out_len -= len; + /* Calculate the next A1 value. */ + if (!HMAC_Final(&ctx_tmp, A1, &A1_len)) { + goto err; + } + } else { + /* Last chunk. */ + if (!HMAC_Final(&ctx, A1, &A1_len)) { + goto err; + } + memcpy(out, A1, out_len); + break; + } + } + + 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(SSL *s, 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) { + size_t idx, len, count, i; + const uint8_t *S1; + long m; + const EVP_MD *md; + int ret = 0; + uint8_t *tmp; + + if (out_len == 0) { + return 1; + } + + /* Allocate a temporary buffer. */ + tmp = OPENSSL_malloc(out_len); + if (tmp == NULL) { + OPENSSL_PUT_ERROR(SSL, tls1_prf, ERR_R_MALLOC_FAILURE); + return 0; + } + + /* Count number of digests and partition |secret| evenly. */ + count = 0; + for (idx = 0; ssl_get_handshake_digest(idx, &m, &md); idx++) { + if ((m << TLS1_PRF_DGST_SHIFT) & ssl_get_algorithm2(s)) { + count++; + } + } + /* TODO(davidben): The only case where count isn't 1 is the old MD5/SHA-1 + * combination. The logic around multiple handshake digests can probably be + * simplified. */ + assert(count == 1 || count == 2); + len = secret_len / count; + if (count == 1) { + secret_len = 0; + } + S1 = secret; + memset(out, 0, out_len); + for (idx = 0; ssl_get_handshake_digest(idx, &m, &md); idx++) { + if ((m << TLS1_PRF_DGST_SHIFT) & ssl_get_algorithm2(s)) { + /* If |count| is 2 and |secret_len| is odd, |secret| is partitioned into + * two halves with an overlapping byte. */ + if (!tls1_P_hash(tmp, out_len, md, S1, len + (secret_len & 1), + (const uint8_t *)label, label_len, seed1, seed1_len, + seed2, seed2_len)) { + goto err; + } + S1 += len; + for (i = 0; i < out_len; i++) { + out[i] ^= tmp[i]; + } + } + } + ret = 1; + +err: + OPENSSL_cleanse(tmp, out_len); + OPENSSL_free(tmp); + return ret; +} + +static int tls1_generate_key_block(SSL *s, uint8_t *out, size_t out_len) { + return s->enc_method->prf(s, out, out_len, s->session->master_key, + s->session->master_key_length, + TLS_MD_KEY_EXPANSION_CONST, + TLS_MD_KEY_EXPANSION_CONST_SIZE, + s->s3->server_random, SSL3_RANDOM_SIZE, + s->s3->client_random, + SSL3_RANDOM_SIZE); +} + +/* tls1_aead_ctx_init allocates |*aead_ctx|, if needed and returns 1. It + * returns 0 on malloc error. */ +static int tls1_aead_ctx_init(SSL_AEAD_CTX **aead_ctx) { + if (*aead_ctx != NULL) { + EVP_AEAD_CTX_cleanup(&(*aead_ctx)->ctx); + } else { + *aead_ctx = (SSL_AEAD_CTX *)OPENSSL_malloc(sizeof(SSL_AEAD_CTX)); + if (*aead_ctx == NULL) { + OPENSSL_PUT_ERROR(SSL, tls1_aead_ctx_init, ERR_R_MALLOC_FAILURE); + return 0; + } + } + + return 1; +} + +static int tls1_change_cipher_state_aead(SSL *s, char is_read, + const uint8_t *key, unsigned key_len, + const uint8_t *iv, unsigned iv_len, + const uint8_t *mac_secret, + unsigned mac_secret_len) { + const EVP_AEAD *aead = s->s3->tmp.new_aead; + SSL_AEAD_CTX *aead_ctx; + /* merged_key is used to merge the MAC, cipher, and IV keys for an AEAD which + * simulates pre-AEAD cipher suites. */ + uint8_t merged_key[EVP_AEAD_MAX_KEY_LENGTH]; + + if (mac_secret_len > 0) { + /* This is a "stateful" AEAD (for compatibility with pre-AEAD cipher + * suites). */ + if (mac_secret_len + key_len + iv_len > sizeof(merged_key)) { + OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead, + ERR_R_INTERNAL_ERROR); + return 0; + } + memcpy(merged_key, mac_secret, mac_secret_len); + memcpy(merged_key + mac_secret_len, key, key_len); + memcpy(merged_key + mac_secret_len + key_len, iv, iv_len); + key = merged_key; + key_len += mac_secret_len; + key_len += iv_len; + } + + if (is_read) { + if (!tls1_aead_ctx_init(&s->aead_read_ctx)) { + return 0; + } + aead_ctx = s->aead_read_ctx; + } else { + /* When updating the cipher state for DTLS, we do not wish to overwrite the + * old ones because DTLS stores pointers to them in order to implement + * retransmission. See dtls1_hm_fragment_free. + * + * TODO(davidben): Simplify aead_write_ctx ownership, probably by just + * forbidding DTLS renego. */ + if (SSL_IS_DTLS(s)) { + s->aead_write_ctx = NULL; + } + if (!tls1_aead_ctx_init(&s->aead_write_ctx)) { + return 0; + } + aead_ctx = s->aead_write_ctx; + } + + if (!EVP_AEAD_CTX_init(&aead_ctx->ctx, aead, key, key_len, + EVP_AEAD_DEFAULT_TAG_LENGTH, NULL /* engine */)) { + OPENSSL_free(aead_ctx); + if (is_read) { + s->aead_read_ctx = NULL; + } else { + s->aead_write_ctx = NULL; + } + + return 0; + } + + if (mac_secret_len == 0) { + /* For a real AEAD, the IV is the fixed part of the nonce. */ + if (iv_len > sizeof(aead_ctx->fixed_nonce)) { + OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead, ERR_R_INTERNAL_ERROR); + return 0; + } + + memcpy(aead_ctx->fixed_nonce, iv, iv_len); + aead_ctx->fixed_nonce_len = iv_len; + aead_ctx->variable_nonce_included_in_record = + (s->s3->tmp.new_cipher->algorithm2 & + SSL_CIPHER_ALGORITHM2_VARIABLE_NONCE_INCLUDED_IN_RECORD) != 0; + aead_ctx->random_variable_nonce = 0; + aead_ctx->omit_length_in_ad = 0; + } else { + aead_ctx->fixed_nonce_len = 0; + aead_ctx->variable_nonce_included_in_record = 1; + aead_ctx->random_variable_nonce = 1; + aead_ctx->omit_length_in_ad = 1; + } + aead_ctx->variable_nonce_len = s->s3->tmp.new_variable_iv_len; + aead_ctx->omit_version_in_ad = (s->version == SSL3_VERSION); + + if (aead_ctx->variable_nonce_len + aead_ctx->fixed_nonce_len != + EVP_AEAD_nonce_length(aead)) { + OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state_aead, ERR_R_INTERNAL_ERROR); + return 0; + } + aead_ctx->tag_len = EVP_AEAD_max_overhead(aead); + + return 1; +} + +int tls1_change_cipher_state(SSL *s, int which) { + /* 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; + const uint8_t *client_write_mac_secret, *server_write_mac_secret, *mac_secret; + const uint8_t *client_write_key, *server_write_key, *key; + const uint8_t *client_write_iv, *server_write_iv, *iv; + const EVP_AEAD *aead = s->s3->tmp.new_aead; + size_t key_len, iv_len, mac_secret_len; + const uint8_t *key_data; + + /* Reset sequence number to zero. */ + if (!SSL_IS_DTLS(s)) { + memset(is_read ? s->s3->read_sequence : s->s3->write_sequence, 0, 8); + } + + mac_secret_len = s->s3->tmp.new_mac_secret_len; + iv_len = s->s3->tmp.new_fixed_iv_len; + + if (aead == NULL) { + OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR); + return 0; + } + + 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 and IV key bytes. */ + if (key_len < mac_secret_len + iv_len) { + OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR); + return 0; + } + key_len -= mac_secret_len + iv_len; + } + + key_data = s->s3->tmp.key_block; + client_write_mac_secret = key_data; + key_data += mac_secret_len; + server_write_mac_secret = key_data; + key_data += mac_secret_len; + client_write_key = key_data; + key_data += key_len; + server_write_key = key_data; + key_data += key_len; + client_write_iv = key_data; + key_data += iv_len; + server_write_iv = key_data; + key_data += iv_len; + + 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; + } + + if (key_data - s->s3->tmp.key_block != s->s3->tmp.key_block_length) { + OPENSSL_PUT_ERROR(SSL, tls1_change_cipher_state, ERR_R_INTERNAL_ERROR); + return 0; + } + + return tls1_change_cipher_state_aead(s, is_read, key, key_len, iv, iv_len, + mac_secret, mac_secret_len); +} + +int tls1_setup_key_block(SSL *s) { + uint8_t *p; + const EVP_AEAD *aead = NULL; + int ret = 0; + size_t mac_secret_len, fixed_iv_len, variable_iv_len, key_len; + size_t key_block_len; + + if (s->s3->tmp.key_block_length != 0) { + return 1; + } + + if (s->session->cipher == NULL) { + goto cipher_unavailable_err; + } + + if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len, + s->session->cipher, + ssl3_version_from_wire(s, s->version))) { + goto cipher_unavailable_err; + } + key_len = EVP_AEAD_key_length(aead); + variable_iv_len = EVP_AEAD_nonce_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, tls1_setup_key_block, ERR_R_INTERNAL_ERROR); + return 0; + } + key_len -= mac_secret_len + fixed_iv_len; + } else { + /* The nonce is split into a fixed portion and a variable portion. */ + if (variable_iv_len < fixed_iv_len) { + OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, ERR_R_INTERNAL_ERROR); + return 0; + } + variable_iv_len -= fixed_iv_len; + } + + assert(mac_secret_len < 256); + assert(fixed_iv_len < 256); + assert(variable_iv_len < 256); + + s->s3->tmp.new_aead = aead; + s->s3->tmp.new_mac_secret_len = (uint8_t)mac_secret_len; + s->s3->tmp.new_fixed_iv_len = (uint8_t)fixed_iv_len; + s->s3->tmp.new_variable_iv_len = (uint8_t)variable_iv_len; + + key_block_len = key_len + mac_secret_len + fixed_iv_len; + key_block_len *= 2; + + ssl3_cleanup_key_block(s); + + p = (uint8_t *)OPENSSL_malloc(key_block_len); + if (p == NULL) { + OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, ERR_R_MALLOC_FAILURE); + goto err; + } + + s->s3->tmp.key_block_length = key_block_len; + s->s3->tmp.key_block = p; + + if (!tls1_generate_key_block(s, p, key_block_len)) { + goto err; + } + + if (!SSL_USE_EXPLICIT_IV(s) && + (s->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0) { + /* enable vulnerability countermeasure for CBC ciphers with known-IV + * problem (http://www.openssl.org/~bodo/tls-cbc.txt). */ + s->s3->need_record_splitting = 1; + + if (s->session->cipher != NULL && + s->session->cipher->algorithm_enc == SSL_RC4) { + s->s3->need_record_splitting = 0; + } + } + + ret = 1; + +err: + return ret; + +cipher_unavailable_err: + OPENSSL_PUT_ERROR(SSL, tls1_setup_key_block, + SSL_R_CIPHER_OR_HASH_UNAVAILABLE); + return 0; +} + +/* tls1_enc encrypts/decrypts the record in |s->wrec| / |s->rrec|, + * respectively. It returns one on success and zero on failure. */ +int tls1_enc(SSL *s, int send) { + SSL3_RECORD *rec; + const SSL_AEAD_CTX *aead; + + if (send) { + rec = &s->s3->wrec; + aead = s->aead_write_ctx; + } else { + rec = &s->s3->rrec; + aead = s->aead_read_ctx; + } + + if (s->session == NULL || aead == NULL) { + /* Handle the initial NULL cipher. */ + memmove(rec->data, rec->input, rec->length); + rec->input = rec->data; + return 1; + } + + uint8_t ad[13], *seq, *in, *out, nonce[EVP_AEAD_MAX_NONCE_LENGTH]; + unsigned nonce_used; + size_t n, ad_len; + + seq = send ? s->s3->write_sequence : s->s3->read_sequence; + + if (SSL_IS_DTLS(s)) { + uint8_t dtlsseq[9], *p = dtlsseq; + + s2n(send ? s->d1->w_epoch : s->d1->r_epoch, p); + memcpy(p, &seq[2], 6); + memcpy(ad, dtlsseq, 8); + } else { + int i; + memcpy(ad, seq, 8); + for (i = 7; i >= 0; i--) { + ++seq[i]; + if (seq[i] != 0) { + break; + } + } + } + + ad[8] = rec->type; + ad_len = 9; + if (!aead->omit_version_in_ad) { + ad[ad_len++] = (uint8_t)(s->version >> 8); + ad[ad_len++] = (uint8_t)(s->version); + } + + if (aead->fixed_nonce_len + aead->variable_nonce_len > sizeof(nonce)) { + OPENSSL_PUT_ERROR(SSL, tls1_enc, ERR_R_INTERNAL_ERROR); + return 0; + } + + memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len); + nonce_used = aead->fixed_nonce_len; + + if (send) { + size_t len = rec->length; + size_t eivlen = 0; + in = rec->input; + out = rec->data; + + uint8_t *variable_nonce = nonce + nonce_used; + if (aead->random_variable_nonce) { + assert(aead->variable_nonce_included_in_record); + if (!RAND_bytes(nonce + nonce_used, aead->variable_nonce_len)) { + return 0; + } + } else { + /* When sending we use the sequence number as the variable part of the + * nonce. */ + if (aead->variable_nonce_len != 8) { + OPENSSL_PUT_ERROR(SSL, tls1_enc, ERR_R_INTERNAL_ERROR); + return 0; + } + memcpy(nonce + nonce_used, ad, aead->variable_nonce_len); + } + nonce_used += aead->variable_nonce_len; + + /* in do_ssl3_write, rec->input is moved forward by variable_nonce_len in + * order to leave space for the variable nonce. Thus we can copy the + * sequence number bytes into place without overwriting any of the + * plaintext. */ + if (aead->variable_nonce_included_in_record) { + memcpy(out, variable_nonce, aead->variable_nonce_len); + len -= aead->variable_nonce_len; + eivlen = aead->variable_nonce_len; + } + + if (!aead->omit_length_in_ad) { + ad[ad_len++] = len >> 8; + ad[ad_len++] = len & 0xff; + } + + if (!EVP_AEAD_CTX_seal(&aead->ctx, out + eivlen, &n, len + aead->tag_len, + nonce, nonce_used, in + eivlen, len, ad, ad_len)) { + return 0; + } + + if (aead->variable_nonce_included_in_record) { + n += aead->variable_nonce_len; + } + } else { + /* receive */ + size_t len = rec->length; + + if (rec->data != rec->input) { + OPENSSL_PUT_ERROR(SSL, tls1_enc, ERR_R_INTERNAL_ERROR); + return 0; + } + out = in = rec->input; + + if (len < aead->variable_nonce_len) { + return 0; + } + memcpy(nonce + nonce_used, + aead->variable_nonce_included_in_record ? in : ad, + aead->variable_nonce_len); + nonce_used += aead->variable_nonce_len; + + if (aead->variable_nonce_included_in_record) { + in += aead->variable_nonce_len; + len -= aead->variable_nonce_len; + out += aead->variable_nonce_len; + } + + if (!aead->omit_length_in_ad) { + if (len < aead->tag_len) { + return 0; + } + size_t plaintext_len = len - aead->tag_len; + + ad[ad_len++] = plaintext_len >> 8; + ad[ad_len++] = plaintext_len & 0xff; + } + + if (!EVP_AEAD_CTX_open(&aead->ctx, out, &n, rec->length, nonce, nonce_used, in, + len, ad, ad_len)) { + return 0; + } + + rec->data = rec->input = out; + } + + rec->length = n; + return 1; +} + +int tls1_cert_verify_mac(SSL *s, int md_nid, uint8_t *out) { + unsigned int ret; + EVP_MD_CTX ctx, *d = NULL; + int i; + + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + return 0; + } + + for (i = 0; i < SSL_MAX_DIGEST; i++) { + if (s->s3->handshake_dgst[i] && + EVP_MD_CTX_type(s->s3->handshake_dgst[i]) == md_nid) { + d = s->s3->handshake_dgst[i]; + break; + } + } + + if (!d) { + OPENSSL_PUT_ERROR(SSL, tls1_cert_verify_mac, SSL_R_NO_REQUIRED_DIGEST); + return 0; + } + + EVP_MD_CTX_init(&ctx); + EVP_MD_CTX_copy_ex(&ctx, d); + EVP_DigestFinal_ex(&ctx, out, &ret); + EVP_MD_CTX_cleanup(&ctx); + + return ret; +} + +/* tls1_handshake_digest calculates the current handshake hash and writes it to + * |out|, which has space for |out_len| bytes. It returns the number of bytes + * written or -1 in the event of an error. This function works on a copy of the + * underlying digests so can be called multiple times and prior to the final + * update etc. */ +int tls1_handshake_digest(SSL *s, uint8_t *out, size_t out_len) { + const EVP_MD *md; + EVP_MD_CTX ctx; + int err = 0, len = 0; + size_t i; + long mask; + + EVP_MD_CTX_init(&ctx); + + for (i = 0; ssl_get_handshake_digest(i, &mask, &md); i++) { + size_t hash_size; + unsigned int digest_len; + EVP_MD_CTX *hdgst = s->s3->handshake_dgst[i]; + + if ((mask & ssl_get_algorithm2(s)) == 0) { + continue; + } + + hash_size = EVP_MD_size(md); + if (!hdgst || + hash_size > out_len || + !EVP_MD_CTX_copy_ex(&ctx, hdgst) || + !EVP_DigestFinal_ex(&ctx, out, &digest_len) || + digest_len != hash_size /* internal error */) { + err = 1; + break; + } + + out += digest_len; + out_len -= digest_len; + len += digest_len; + } + + EVP_MD_CTX_cleanup(&ctx); + + if (err != 0) { + return -1; + } + return len; +} + +int tls1_final_finish_mac(SSL *s, const char *str, int slen, uint8_t *out) { + uint8_t buf[2 * EVP_MAX_MD_SIZE]; + int err = 0; + int digests_len; + + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + return 0; + } + + digests_len = tls1_handshake_digest(s, buf, sizeof(buf)); + if (digests_len < 0) { + err = 1; + digests_len = 0; + } + + if (!s->enc_method->prf(s, out, 12, s->session->master_key, + s->session->master_key_length, str, slen, buf, + digests_len, NULL, 0)) { + err = 1; + } + + if (err) { + return 0; + } else { + return 12; + } +} + +int tls1_generate_master_secret(SSL *s, uint8_t *out, const uint8_t *premaster, + size_t premaster_len) { + if (s->s3->tmp.extended_master_secret) { + uint8_t digests[2 * EVP_MAX_MD_SIZE]; + int digests_len; + + /* The master secret is based on the handshake hash just after sending the + * ClientKeyExchange. However, we might have a client certificate to send, + * in which case we might need different hashes for the verification and + * thus still need the handshake buffer around. Keeping both a handshake + * buffer *and* running hashes isn't yet supported so, when it comes to + * calculating the Finished hash, we'll have to hash the handshake buffer + * again. */ + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, dont_free_handshake_buffer)) { + return 0; + } + + digests_len = tls1_handshake_digest(s, digests, sizeof(digests)); + if (digests_len == -1) { + return 0; + } + + if (!s->enc_method->prf(s, 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 (!s->enc_method->prf(s, out, SSL3_MASTER_SECRET_SIZE, premaster, + premaster_len, TLS_MD_MASTER_SECRET_CONST, + TLS_MD_MASTER_SECRET_CONST_SIZE, + s->s3->client_random, SSL3_RANDOM_SIZE, + s->s3->server_random, SSL3_RANDOM_SIZE)) { + return 0; + } + } + + return SSL3_MASTER_SECRET_SIZE; +} + +int tls1_export_keying_material(SSL *s, uint8_t *out, size_t olen, + const char *label, size_t llen, + const uint8_t *context, size_t contextlen, + int use_context) { + uint8_t *val = NULL; + size_t vallen, currentvalpos; + int ret; + + /* construct PRF arguments we construct the PRF argument ourself rather than + * passing separate values into the TLS PRF to ensure that the concatenation + * of values does not create a prohibited label. */ + vallen = llen + SSL3_RANDOM_SIZE * 2; + if (use_context) { + vallen += 2 + contextlen; + } + + val = OPENSSL_malloc(vallen); + if (val == NULL) { + goto err2; + } + + currentvalpos = 0; + memcpy(val + currentvalpos, (uint8_t *)label, llen); + currentvalpos += llen; + memcpy(val + currentvalpos, s->s3->client_random, SSL3_RANDOM_SIZE); + currentvalpos += SSL3_RANDOM_SIZE; + memcpy(val + currentvalpos, s->s3->server_random, SSL3_RANDOM_SIZE); + currentvalpos += SSL3_RANDOM_SIZE; + + if (use_context) { + val[currentvalpos] = (contextlen >> 8) & 0xff; + currentvalpos++; + val[currentvalpos] = contextlen & 0xff; + currentvalpos++; + if (contextlen > 0 || context != NULL) { + memcpy(val + currentvalpos, context, contextlen); + } + } + + /* disallow prohibited labels note that SSL3_RANDOM_SIZE > max(prohibited + * label len) = 15, so size of val > max(prohibited label len) = 15 and the + * comparisons won't have buffer overflow. */ + if (memcmp(val, TLS_MD_CLIENT_FINISH_CONST, + TLS_MD_CLIENT_FINISH_CONST_SIZE) == 0 || + memcmp(val, TLS_MD_SERVER_FINISH_CONST, + TLS_MD_SERVER_FINISH_CONST_SIZE) == 0 || + memcmp(val, TLS_MD_MASTER_SECRET_CONST, + TLS_MD_MASTER_SECRET_CONST_SIZE) == 0 || + memcmp(val, TLS_MD_KEY_EXPANSION_CONST, + TLS_MD_KEY_EXPANSION_CONST_SIZE) == 0) { + goto err1; + } + + /* SSL_export_keying_material is not implemented for SSLv3, so passing + * everything through the label parameter works. */ + assert(s->version != SSL3_VERSION); + ret = s->enc_method->prf(s, out, olen, s->session->master_key, + s->session->master_key_length, (const char *)val, + vallen, NULL, 0, NULL, 0); + goto out; + +err1: + OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material, + SSL_R_TLS_ILLEGAL_EXPORTER_LABEL); + ret = 0; + goto out; + +err2: + OPENSSL_PUT_ERROR(SSL, tls1_export_keying_material, ERR_R_MALLOC_FAILURE); + ret = 0; + +out: + if (val != NULL) { + OPENSSL_free(val); + } + + return ret; +} + +int tls1_alert_code(int code) { + switch (code) { + case SSL_AD_CLOSE_NOTIFY: + return SSL3_AD_CLOSE_NOTIFY; + + case SSL_AD_UNEXPECTED_MESSAGE: + return SSL3_AD_UNEXPECTED_MESSAGE; + + case SSL_AD_BAD_RECORD_MAC: + return SSL3_AD_BAD_RECORD_MAC; + + case SSL_AD_DECRYPTION_FAILED: + return TLS1_AD_DECRYPTION_FAILED; + + case SSL_AD_RECORD_OVERFLOW: + return TLS1_AD_RECORD_OVERFLOW; + + case SSL_AD_DECOMPRESSION_FAILURE: + return SSL3_AD_DECOMPRESSION_FAILURE; + + case SSL_AD_HANDSHAKE_FAILURE: + return SSL3_AD_HANDSHAKE_FAILURE; + + case SSL_AD_NO_CERTIFICATE: + return -1; + + case SSL_AD_BAD_CERTIFICATE: + return SSL3_AD_BAD_CERTIFICATE; + + case SSL_AD_UNSUPPORTED_CERTIFICATE: + return SSL3_AD_UNSUPPORTED_CERTIFICATE; + + case SSL_AD_CERTIFICATE_REVOKED: + return SSL3_AD_CERTIFICATE_REVOKED; + + case SSL_AD_CERTIFICATE_EXPIRED: + return SSL3_AD_CERTIFICATE_EXPIRED; + + case SSL_AD_CERTIFICATE_UNKNOWN: + return SSL3_AD_CERTIFICATE_UNKNOWN; + + case SSL_AD_ILLEGAL_PARAMETER: + return SSL3_AD_ILLEGAL_PARAMETER; + + case SSL_AD_UNKNOWN_CA: + return TLS1_AD_UNKNOWN_CA; + + case SSL_AD_ACCESS_DENIED: + return TLS1_AD_ACCESS_DENIED; + + case SSL_AD_DECODE_ERROR: + return TLS1_AD_DECODE_ERROR; + + case SSL_AD_DECRYPT_ERROR: + return TLS1_AD_DECRYPT_ERROR; + case SSL_AD_EXPORT_RESTRICTION: + return TLS1_AD_EXPORT_RESTRICTION; + + case SSL_AD_PROTOCOL_VERSION: + return TLS1_AD_PROTOCOL_VERSION; + + case SSL_AD_INSUFFICIENT_SECURITY: + return TLS1_AD_INSUFFICIENT_SECURITY; + + case SSL_AD_INTERNAL_ERROR: + return TLS1_AD_INTERNAL_ERROR; + + case SSL_AD_USER_CANCELLED: + return TLS1_AD_USER_CANCELLED; + + case SSL_AD_NO_RENEGOTIATION: + return TLS1_AD_NO_RENEGOTIATION; + + case SSL_AD_UNSUPPORTED_EXTENSION: + return TLS1_AD_UNSUPPORTED_EXTENSION; + + case SSL_AD_CERTIFICATE_UNOBTAINABLE: + return TLS1_AD_CERTIFICATE_UNOBTAINABLE; + + case SSL_AD_UNRECOGNIZED_NAME: + return TLS1_AD_UNRECOGNIZED_NAME; + + case SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE: + return TLS1_AD_BAD_CERTIFICATE_STATUS_RESPONSE; + + case SSL_AD_BAD_CERTIFICATE_HASH_VALUE: + return TLS1_AD_BAD_CERTIFICATE_HASH_VALUE; + + case SSL_AD_UNKNOWN_PSK_IDENTITY: + return TLS1_AD_UNKNOWN_PSK_IDENTITY; + + case SSL_AD_INAPPROPRIATE_FALLBACK: + return SSL3_AD_INAPPROPRIATE_FALLBACK; + + default: + return -1; + } +} diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c new file mode 100644 index 0000000..e26351b --- /dev/null +++ b/src/ssl/t1_lib.c @@ -0,0 +1,2671 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <assert.h> + +#include <openssl/bytestring.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/rand.h> + +#include "ssl_locl.h" + + +static int tls_decrypt_ticket(SSL *s, const uint8_t *tick, int ticklen, + const uint8_t *sess_id, int sesslen, + SSL_SESSION **psess); +static int ssl_check_clienthello_tlsext(SSL *s); +static int ssl_check_serverhello_tlsext(SSL *s); + +const SSL3_ENC_METHOD TLSv1_enc_data = { + tls1_enc, + tls1_prf, + tls1_setup_key_block, + tls1_generate_master_secret, + tls1_change_cipher_state, + tls1_final_finish_mac, + TLS1_FINISH_MAC_LENGTH, + tls1_cert_verify_mac, + TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE, + TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE, + tls1_alert_code, + tls1_export_keying_material, + 0, + SSL3_HM_HEADER_LENGTH, + ssl3_set_handshake_header, + ssl3_handshake_write, +}; + +const SSL3_ENC_METHOD TLSv1_1_enc_data = { + tls1_enc, + tls1_prf, + tls1_setup_key_block, + tls1_generate_master_secret, + tls1_change_cipher_state, + tls1_final_finish_mac, + TLS1_FINISH_MAC_LENGTH, + tls1_cert_verify_mac, + TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE, + TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE, + tls1_alert_code, + tls1_export_keying_material, + SSL_ENC_FLAG_EXPLICIT_IV, + SSL3_HM_HEADER_LENGTH, + ssl3_set_handshake_header, + ssl3_handshake_write, +}; + +const SSL3_ENC_METHOD TLSv1_2_enc_data = { + tls1_enc, + tls1_prf, + tls1_setup_key_block, + tls1_generate_master_secret, + tls1_change_cipher_state, + tls1_final_finish_mac, + TLS1_FINISH_MAC_LENGTH, + tls1_cert_verify_mac, + TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE, + TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE, + tls1_alert_code, + tls1_export_keying_material, + SSL_ENC_FLAG_EXPLICIT_IV|SSL_ENC_FLAG_SIGALGS|SSL_ENC_FLAG_SHA256_PRF + |SSL_ENC_FLAG_TLS1_2_CIPHERS, + SSL3_HM_HEADER_LENGTH, + ssl3_set_handshake_header, + ssl3_handshake_write, +}; + +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 = + (uint16_t *)OPENSSL_malloc(sizeof(uint16_t) * num_extensions); + if (extension_types == NULL) { + OPENSSL_PUT_ERROR(SSL, tls1_check_duplicate_extensions, + 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: + if (extension_types) + OPENSSL_free(extension_types); + return ret; +} + +char ssl_early_callback_init(struct ssl_early_callback_ctx *ctx) { + CBS client_hello, session_id, cipher_suites, compression_methods, extensions; + + CBS_init(&client_hello, ctx->client_hello, ctx->client_hello_len); + + if (/* Skip client version. */ + !CBS_skip(&client_hello, 2) || + /* Skip client nonce. */ + !CBS_skip(&client_hello, 32) || + /* Extract session_id. */ + !CBS_get_u8_length_prefixed(&client_hello, &session_id)) { + return 0; + } + + ctx->session_id = CBS_data(&session_id); + ctx->session_id_len = CBS_len(&session_id); + + /* Skip past DTLS cookie */ + if (SSL_IS_DTLS(ctx->ssl)) { + CBS cookie; + + if (!CBS_get_u8_length_prefixed(&client_hello, &cookie)) { + return 0; + } + } + + /* Extract cipher_suites. */ + if (!CBS_get_u16_length_prefixed(&client_hello, &cipher_suites) || + CBS_len(&cipher_suites) < 2 || (CBS_len(&cipher_suites) & 1) != 0) { + return 0; + } + ctx->cipher_suites = CBS_data(&cipher_suites); + ctx->cipher_suites_len = CBS_len(&cipher_suites); + + /* Extract compression_methods. */ + if (!CBS_get_u8_length_prefixed(&client_hello, &compression_methods) || + CBS_len(&compression_methods) < 1) { + return 0; + } + ctx->compression_methods = CBS_data(&compression_methods); + ctx->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) { + ctx->extensions = NULL; + ctx->extensions_len = 0; + return 1; + } + + /* Extract extensions and check it is valid. */ + if (!CBS_get_u16_length_prefixed(&client_hello, &extensions) || + !tls1_check_duplicate_extensions(&extensions) || + CBS_len(&client_hello) != 0) { + return 0; + } + ctx->extensions = CBS_data(&extensions); + ctx->extensions_len = CBS_len(&extensions); + + return 1; +} + +char SSL_early_callback_ctx_extension_get( + const struct ssl_early_callback_ctx *ctx, uint16_t extension_type, + const uint8_t **out_data, size_t *out_len) { + CBS extensions; + + CBS_init(&extensions, ctx->extensions, ctx->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)) { + return 0; + } + + if (type == extension_type) { + *out_data = CBS_data(&extension); + *out_len = CBS_len(&extension); + return 1; + } + } + + return 0; +} + +struct tls_curve { + uint16_t curve_id; + int nid; +}; + +/* ECC curves from RFC4492. */ +static const struct tls_curve tls_curves[] = { + {21, NID_secp224r1}, + {23, NID_X9_62_prime256v1}, + {24, NID_secp384r1}, + {25, NID_secp521r1}, +}; + +static const uint8_t ecformats_default[] = { + TLSEXT_ECPOINTFORMAT_uncompressed, +}; + +static const uint16_t eccurves_default[] = { + 23, /* X9_64_prime256v1 */ + 24, /* secp384r1 */ +}; + +int tls1_ec_curve_id2nid(uint16_t curve_id) { + size_t i; + for (i = 0; i < sizeof(tls_curves) / sizeof(tls_curves[0]); i++) { + if (curve_id == tls_curves[i].curve_id) { + return tls_curves[i].nid; + } + } + return NID_undef; +} + +int tls1_ec_nid2curve_id(uint16_t *out_curve_id, int nid) { + size_t i; + for (i = 0; i < sizeof(tls_curves) / sizeof(tls_curves[0]); i++) { + if (nid == tls_curves[i].nid) { + *out_curve_id = tls_curves[i].curve_id; + return 1; + } + } + return 0; +} + +/* tls1_get_curvelist sets |*out_curve_ids| and |*out_curve_ids_len| to the + * list of allowed curve IDs. If |get_peer_curves| is non-zero, return the + * peer's curve list. Otherwise, return the preferred list. */ +static void tls1_get_curvelist(SSL *s, int get_peer_curves, + const uint16_t **out_curve_ids, + size_t *out_curve_ids_len) { + if (get_peer_curves) { + *out_curve_ids = s->s3->tmp.peer_ellipticcurvelist; + *out_curve_ids_len = s->s3->tmp.peer_ellipticcurvelist_length; + return; + } + + *out_curve_ids = s->tlsext_ellipticcurvelist; + *out_curve_ids_len = s->tlsext_ellipticcurvelist_length; + if (!*out_curve_ids) { + *out_curve_ids = eccurves_default; + *out_curve_ids_len = sizeof(eccurves_default) / sizeof(eccurves_default[0]); + } +} + +int tls1_check_curve(SSL *s, CBS *cbs, uint16_t *out_curve_id) { + uint8_t curve_type; + uint16_t curve_id; + const uint16_t *curves; + size_t curves_len, i; + + /* Only support named curves. */ + if (!CBS_get_u8(cbs, &curve_type) || + curve_type != NAMED_CURVE_TYPE || + !CBS_get_u16(cbs, &curve_id)) { + return 0; + } + + tls1_get_curvelist(s, 0, &curves, &curves_len); + for (i = 0; i < curves_len; i++) { + if (curve_id == curves[i]) { + *out_curve_id = curve_id; + return 1; + } + } + + return 0; +} + +int tls1_get_shared_curve(SSL *s) { + const uint16_t *pref, *supp; + size_t preflen, supplen, i, j; + + /* Can't do anything on client side */ + if (s->server == 0) { + return NID_undef; + } + + /* Return first preference shared curve */ + tls1_get_curvelist(s, !!(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE), &supp, + &supplen); + tls1_get_curvelist(s, !(s->options & SSL_OP_CIPHER_SERVER_PREFERENCE), &pref, + &preflen); + + for (i = 0; i < preflen; i++) { + for (j = 0; j < supplen; j++) { + if (pref[i] == supp[j]) { + return tls1_ec_curve_id2nid(pref[i]); + } + } + } + + return NID_undef; +} + +int tls1_set_curves(uint16_t **out_curve_ids, size_t *out_curve_ids_len, + const int *curves, size_t ncurves) { + uint16_t *curve_ids; + size_t i; + + curve_ids = (uint16_t *)OPENSSL_malloc(ncurves * sizeof(uint16_t)); + if (curve_ids == NULL) { + return 0; + } + + for (i = 0; i < ncurves; i++) { + if (!tls1_ec_nid2curve_id(&curve_ids[i], curves[i])) { + OPENSSL_free(curve_ids); + return 0; + } + } + + if (*out_curve_ids) { + OPENSSL_free(*out_curve_ids); + } + *out_curve_ids = curve_ids; + *out_curve_ids_len = ncurves; + + return 1; +} + +/* tls1_curve_params_from_ec_key sets |*out_curve_id| and |*out_comp_id| to the + * TLS curve ID and point format, respectively, for |ec|. It returns one on + * success and zero on failure. */ +static int tls1_curve_params_from_ec_key(uint16_t *out_curve_id, + uint8_t *out_comp_id, EC_KEY *ec) { + int nid; + uint16_t id; + const EC_GROUP *grp; + + if (ec == NULL) { + return 0; + } + + grp = EC_KEY_get0_group(ec); + if (grp == NULL) { + return 0; + } + + /* Determine curve ID */ + nid = EC_GROUP_get_curve_name(grp); + if (!tls1_ec_nid2curve_id(&id, nid)) { + return 0; + } + + /* Set the named curve ID. Arbitrary explicit curves are not supported. */ + *out_curve_id = id; + + if (out_comp_id) { + if (EC_KEY_get0_public_key(ec) == NULL) { + return 0; + } + if (EC_KEY_get_conv_form(ec) == POINT_CONVERSION_COMPRESSED) { + *out_comp_id = TLSEXT_ECPOINTFORMAT_ansiX962_compressed_prime; + } else { + *out_comp_id = TLSEXT_ECPOINTFORMAT_uncompressed; + } + } + + return 1; +} + +/* tls1_check_point_format returns one if |comp_id| is consistent with the + * peer's point format preferences. */ +static int tls1_check_point_format(SSL *s, uint8_t comp_id) { + uint8_t *p = s->s3->tmp.peer_ecpointformatlist; + size_t plen = s->s3->tmp.peer_ecpointformatlist_length; + size_t i; + + /* If point formats extension present check it, otherwise everything is + * supported (see RFC4492). */ + if (p == NULL) { + return 1; + } + + for (i = 0; i < plen; i++) { + if (comp_id == p[i]) { + return 1; + } + } + + return 0; +} + +/* tls1_check_curve_id returns one if |curve_id| is consistent with both our + * and the peer's curve preferences. Note: if called as the client, only our + * preferences are checked; the peer (the server) does not send preferences. */ +static int tls1_check_curve_id(SSL *s, uint16_t curve_id) { + const uint16_t *curves; + size_t curves_len, i, j; + + /* Check against our list, then the peer's list. */ + for (j = 0; j <= 1; j++) { + tls1_get_curvelist(s, j, &curves, &curves_len); + for (i = 0; i < curves_len; i++) { + if (curves[i] == curve_id) { + break; + } + } + + if (i == curves_len) { + return 0; + } + + /* Servers do not present a preference list so, if we are a client, only + * check our list. */ + if (!s->server) { + return 1; + } + } + + return 1; +} + +static void tls1_get_formatlist(SSL *s, const uint8_t **pformats, + size_t *pformatslen) { + /* If we have a custom point format list use it otherwise use default */ + if (s->tlsext_ecpointformatlist) { + *pformats = s->tlsext_ecpointformatlist; + *pformatslen = s->tlsext_ecpointformatlist_length; + } else { + *pformats = ecformats_default; + *pformatslen = sizeof(ecformats_default); + } +} + +int tls1_check_ec_cert(SSL *s, X509 *x) { + int ret = 0; + EVP_PKEY *pkey = X509_get_pubkey(x); + uint16_t curve_id; + uint8_t comp_id; + + if (!pkey || + pkey->type != EVP_PKEY_EC || + !tls1_curve_params_from_ec_key(&curve_id, &comp_id, pkey->pkey.ec) || + !tls1_check_curve_id(s, curve_id) || + !tls1_check_point_format(s, comp_id)) { + goto done; + } + + ret = 1; + +done: + if (pkey) { + EVP_PKEY_free(pkey); + } + return ret; +} + +int tls1_check_ec_tmp_key(SSL *s) { + uint16_t curve_id; + EC_KEY *ec = s->cert->ecdh_tmp; + + if (s->cert->ecdh_tmp_auto) { + /* Need a shared curve */ + return tls1_get_shared_curve(s) != NID_undef; + } + + if (!ec) { + if (s->cert->ecdh_tmp_cb) { + return 1; + } + return 0; + } + + return tls1_curve_params_from_ec_key(&curve_id, NULL, ec) && + tls1_check_curve_id(s, curve_id); +} + +/* List of supported signature algorithms and hashes. Should make this + * customisable at some point, for now include everything we support. */ + +#define tlsext_sigalg_rsa(md) md, TLSEXT_signature_rsa, + +#define tlsext_sigalg_ecdsa(md) md, TLSEXT_signature_ecdsa, + +#define tlsext_sigalg(md) tlsext_sigalg_rsa(md) tlsext_sigalg_ecdsa(md) + +static const uint8_t tls12_sigalgs[] = { + tlsext_sigalg(TLSEXT_hash_sha512) + tlsext_sigalg(TLSEXT_hash_sha384) + tlsext_sigalg(TLSEXT_hash_sha256) + tlsext_sigalg(TLSEXT_hash_sha224) + tlsext_sigalg(TLSEXT_hash_sha1) +}; + +size_t tls12_get_psigalgs(SSL *s, const uint8_t **psigs) { + /* If server use client authentication sigalgs if not NULL */ + if (s->server && s->cert->client_sigalgs) { + *psigs = s->cert->client_sigalgs; + return s->cert->client_sigalgslen; + } else if (s->cert->conf_sigalgs) { + *psigs = s->cert->conf_sigalgs; + return s->cert->conf_sigalgslen; + } else { + *psigs = tls12_sigalgs; + return sizeof(tls12_sigalgs); + } +} + +/* tls12_check_peer_sigalg parses a SignatureAndHashAlgorithm out of |cbs|. It + * checks it is consistent with |s|'s sent supported signature algorithms and, + * if so, writes the relevant digest into |*out_md| and returns 1. Otherwise it + * returns 0 and writes an alert into |*out_alert|. */ +int tls12_check_peer_sigalg(const EVP_MD **out_md, int *out_alert, SSL *s, + CBS *cbs, EVP_PKEY *pkey) { + const uint8_t *sent_sigs; + size_t sent_sigslen, i; + int sigalg = tls12_get_sigid(pkey); + uint8_t hash, signature; + + /* Should never happen */ + if (sigalg == -1) { + OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, ERR_R_INTERNAL_ERROR); + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + if (!CBS_get_u8(cbs, &hash) || + !CBS_get_u8(cbs, &signature)) { + OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_DECODE_ERROR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Check key type is consistent with signature */ + if (sigalg != signature) { + OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_WRONG_SIGNATURE_TYPE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + if (pkey->type == EVP_PKEY_EC) { + uint16_t curve_id; + uint8_t comp_id; + /* Check compression and curve matches extensions */ + if (!tls1_curve_params_from_ec_key(&curve_id, &comp_id, pkey->pkey.ec)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + if (s->server && (!tls1_check_curve_id(s, curve_id) || + !tls1_check_point_format(s, comp_id))) { + OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_WRONG_CURVE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + } + + /* Check signature matches a type we sent */ + sent_sigslen = tls12_get_psigalgs(s, &sent_sigs); + for (i = 0; i < sent_sigslen; i += 2, sent_sigs += 2) { + if (hash == sent_sigs[0] && signature == sent_sigs[1]) { + break; + } + } + + /* Allow fallback to SHA-1. */ + if (i == sent_sigslen && hash != TLSEXT_hash_sha1) { + OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_WRONG_SIGNATURE_TYPE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + *out_md = tls12_get_hash(hash); + if (*out_md == NULL) { + OPENSSL_PUT_ERROR(SSL, tls12_check_peer_sigalg, SSL_R_UNKNOWN_DIGEST); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + + return 1; +} + +/* Get a mask of disabled algorithms: an algorithm is disabled if it isn't + * supported or doesn't appear in supported signature algorithms. Unlike + * ssl_cipher_get_disabled this applies to a specific session and not global + * settings. */ +void ssl_set_client_disabled(SSL *s) { + CERT *c = s->cert; + const uint8_t *sigalgs; + size_t i, sigalgslen; + int have_rsa = 0, have_ecdsa = 0; + c->mask_a = 0; + c->mask_k = 0; + + /* Don't allow TLS 1.2 only ciphers if we don't suppport them */ + if (!SSL_CLIENT_USE_TLS1_2_CIPHERS(s)) { + c->mask_ssl = SSL_TLSV1_2; + } else { + c->mask_ssl = 0; + } + + /* Now go through all signature algorithms seeing if we support any for RSA, + * DSA, ECDSA. Do this for all versions not just TLS 1.2. */ + sigalgslen = tls12_get_psigalgs(s, &sigalgs); + for (i = 0; i < sigalgslen; i += 2, sigalgs += 2) { + switch (sigalgs[1]) { + case TLSEXT_signature_rsa: + have_rsa = 1; + break; + + case TLSEXT_signature_ecdsa: + have_ecdsa = 1; + break; + } + } + + /* Disable auth if we don't include any appropriate signature algorithms. */ + if (!have_rsa) { + c->mask_a |= SSL_aRSA; + } + if (!have_ecdsa) { + c->mask_a |= SSL_aECDSA; + } + + /* with PSK there must be client callback set */ + if (!s->psk_client_callback) { + c->mask_a |= SSL_aPSK; + c->mask_k |= SSL_kPSK; + } +} + +/* header_len is the length of the ClientHello header written so far, used to + * compute padding. It does not include the record header. Pass 0 if no padding + * is to be done. */ +uint8_t *ssl_add_clienthello_tlsext(SSL *s, uint8_t *buf, uint8_t *limit, + size_t header_len) { + int extdatalen = 0; + uint8_t *ret = buf; + uint8_t *orig = buf; + /* See if we support any ECC ciphersuites */ + int using_ecc = 0; + + if (s->version >= TLS1_VERSION || SSL_IS_DTLS(s)) { + size_t i; + unsigned long alg_k, alg_a; + STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(s); + + for (i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) { + const SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i); + + alg_k = c->algorithm_mkey; + alg_a = c->algorithm_auth; + if ((alg_k & SSL_kEECDH) || (alg_a & SSL_aECDSA)) { + using_ecc = 1; + break; + } + } + } + + /* don't add extensions for SSLv3 unless doing secure renegotiation */ + if (s->client_version == SSL3_VERSION && !s->s3->send_connection_binding) { + return orig; + } + + ret += 2; + + if (ret >= limit) { + return NULL; /* should never occur. */ + } + + if (s->tlsext_hostname != NULL) { + /* Add TLS extension servername to the Client Hello message */ + unsigned long size_str; + long lenmax; + + /* check for enough space. + 4 for the servername type and entension length + 2 for servernamelist length + 1 for the hostname type + 2 for hostname length + + hostname length */ + + lenmax = limit - ret - 9; + size_str = strlen(s->tlsext_hostname); + if (lenmax < 0 || size_str > (unsigned long)lenmax) { + return NULL; + } + + /* extension type and length */ + s2n(TLSEXT_TYPE_server_name, ret); + s2n(size_str + 5, ret); + + /* length of servername list */ + s2n(size_str + 3, ret); + + /* hostname type, length and hostname */ + *(ret++) = (uint8_t)TLSEXT_NAMETYPE_host_name; + s2n(size_str, ret); + memcpy(ret, s->tlsext_hostname, size_str); + ret += size_str; + } + + /* Add RI if renegotiating */ + if (s->renegotiate) { + int el; + + if (!ssl_add_clienthello_renegotiate_ext(s, 0, &el, 0)) { + OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + + if ((limit - ret - 4 - el) < 0) { + return NULL; + } + + s2n(TLSEXT_TYPE_renegotiate, ret); + s2n(el, ret); + + if (!ssl_add_clienthello_renegotiate_ext(s, ret, &el, el)) { + OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + + ret += el; + } + + /* Add extended master secret. */ + if (s->version != SSL3_VERSION) { + if (limit - ret - 4 < 0) { + return NULL; + } + s2n(TLSEXT_TYPE_extended_master_secret, ret); + s2n(0, ret); + } + + if (!(SSL_get_options(s) & SSL_OP_NO_TICKET)) { + int ticklen = 0; + if (!s->new_session && s->session && s->session->tlsext_tick) { + ticklen = s->session->tlsext_ticklen; + } + + /* Check for enough room 2 for extension type, 2 for len rest for + * ticket. */ + if ((long)(limit - ret - 4 - ticklen) < 0) { + return NULL; + } + s2n(TLSEXT_TYPE_session_ticket, ret); + s2n(ticklen, ret); + if (ticklen) { + memcpy(ret, s->session->tlsext_tick, ticklen); + ret += ticklen; + } + } + + if (ssl3_version_from_wire(s, s->client_version) >= TLS1_2_VERSION) { + size_t salglen; + const uint8_t *salg; + salglen = tls12_get_psigalgs(s, &salg); + if ((size_t)(limit - ret) < salglen + 6) { + return NULL; + } + s2n(TLSEXT_TYPE_signature_algorithms, ret); + s2n(salglen + 2, ret); + s2n(salglen, ret); + memcpy(ret, salg, salglen); + ret += salglen; + } + + if (s->ocsp_stapling_enabled) { + /* The status_request extension is excessively extensible at every layer. + * On the client, only support requesting OCSP responses with an empty + * responder_id_list and no extensions. */ + if (limit - ret - 4 - 1 - 2 - 2 < 0) { + return NULL; + } + + s2n(TLSEXT_TYPE_status_request, ret); + s2n(1 + 2 + 2, ret); + /* status_type */ + *(ret++) = TLSEXT_STATUSTYPE_ocsp; + /* responder_id_list - empty */ + s2n(0, ret); + /* request_extensions - empty */ + s2n(0, ret); + } + + if (s->ctx->next_proto_select_cb && !s->s3->tmp.finish_md_len && + !SSL_IS_DTLS(s)) { + /* The client advertises an emtpy extension to indicate its support for + * Next Protocol Negotiation */ + if (limit - ret - 4 < 0) { + return NULL; + } + s2n(TLSEXT_TYPE_next_proto_neg, ret); + s2n(0, ret); + } + + if (s->signed_cert_timestamps_enabled && !s->s3->tmp.finish_md_len) { + /* The client advertises an empty extension to indicate its support for + * certificate timestamps. */ + if (limit - ret - 4 < 0) { + return NULL; + } + s2n(TLSEXT_TYPE_certificate_timestamp, ret); + s2n(0, ret); + } + + if (s->alpn_client_proto_list && !s->s3->tmp.finish_md_len) { + if ((size_t)(limit - ret) < 6 + s->alpn_client_proto_list_len) { + return NULL; + } + s2n(TLSEXT_TYPE_application_layer_protocol_negotiation, ret); + s2n(2 + s->alpn_client_proto_list_len, ret); + s2n(s->alpn_client_proto_list_len, ret); + memcpy(ret, s->alpn_client_proto_list, s->alpn_client_proto_list_len); + ret += s->alpn_client_proto_list_len; + } + + if (s->tlsext_channel_id_enabled && !SSL_IS_DTLS(s)) { + /* The client advertises an emtpy extension to indicate its support for + * Channel ID. */ + if (limit - ret - 4 < 0) { + return NULL; + } + if (s->ctx->tlsext_channel_id_enabled_new) { + s2n(TLSEXT_TYPE_channel_id_new, ret); + } else { + s2n(TLSEXT_TYPE_channel_id, ret); + } + s2n(0, ret); + } + + if (SSL_get_srtp_profiles(s)) { + int el; + + ssl_add_clienthello_use_srtp_ext(s, 0, &el, 0); + + if ((limit - ret - 4 - el) < 0) { + return NULL; + } + + s2n(TLSEXT_TYPE_use_srtp, ret); + s2n(el, ret); + + if (!ssl_add_clienthello_use_srtp_ext(s, ret, &el, el)) { + OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + ret += el; + } + + if (using_ecc) { + /* Add TLS extension ECPointFormats to the ClientHello message */ + long lenmax; + const uint8_t *formats; + const uint16_t *curves; + size_t formats_len, curves_len, i; + + tls1_get_formatlist(s, &formats, &formats_len); + + lenmax = limit - ret - 5; + if (lenmax < 0) { + return NULL; + } + if (formats_len > (size_t)lenmax) { + return NULL; + } + if (formats_len > 255) { + OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + + s2n(TLSEXT_TYPE_ec_point_formats, ret); + s2n(formats_len + 1, ret); + *(ret++) = (uint8_t)formats_len; + memcpy(ret, formats, formats_len); + ret += formats_len; + + /* Add TLS extension EllipticCurves to the ClientHello message */ + tls1_get_curvelist(s, 0, &curves, &curves_len); + + lenmax = limit - ret - 6; + if (lenmax < 0) { + return NULL; + } + if (curves_len * 2 > (size_t)lenmax) { + return NULL; + } + if (curves_len * 2 > 65532) { + OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + + s2n(TLSEXT_TYPE_elliptic_curves, ret); + s2n((curves_len * 2) + 2, ret); + + s2n(curves_len * 2, ret); + for (i = 0; i < curves_len; i++) { + s2n(curves[i], ret); + } + } + + if (header_len > 0) { + size_t clienthello_minsize = 0; + header_len += ret - orig; + if (header_len > 0xff && header_len < 0x200) { + /* Add padding to workaround bugs in F5 terminators. See + * https://tools.ietf.org/html/draft-agl-tls-padding-03 + * + * NB: because this code works out the length of all existing extensions + * it MUST always appear last. */ + clienthello_minsize = 0x200; + } + if (s->fastradio_padding) { + /* Pad the ClientHello record to 1024 bytes to fast forward the radio + * into DCH (high data rate) state in 3G networks. Note that when + * fastradio_padding is enabled, even if the header_len is less than 255 + * bytes, the padding will be applied regardless. This is slightly + * different from the TLS padding extension suggested in + * https://tools.ietf.org/html/draft-agl-tls-padding-03 */ + clienthello_minsize = 0x400; + } + if (header_len < clienthello_minsize) { + size_t padding_len = clienthello_minsize - header_len; + /* Extensions take at least four bytes to encode. Always include least + * one byte of data if including the extension. WebSphere Application + * Server 7.0 is intolerant to the last extension being zero-length. */ + if (padding_len >= 4 + 1) { + padding_len -= 4; + } else { + padding_len = 1; + } + + if (limit - ret - 4 - (long)padding_len < 0) { + return NULL; + } + + s2n(TLSEXT_TYPE_padding, ret); + s2n(padding_len, ret); + memset(ret, 0, padding_len); + ret += padding_len; + } + } + + extdatalen = ret - orig - 2; + if (extdatalen == 0) { + return orig; + } + + s2n(extdatalen, orig); + return ret; +} + +uint8_t *ssl_add_serverhello_tlsext(SSL *s, uint8_t *buf, uint8_t *limit) { + int extdatalen = 0; + uint8_t *orig = buf; + uint8_t *ret = buf; + int next_proto_neg_seen; + unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + unsigned long alg_a = s->s3->tmp.new_cipher->algorithm_auth; + int using_ecc = (alg_k & SSL_kEECDH) || (alg_a & SSL_aECDSA); + using_ecc = using_ecc && (s->s3->tmp.peer_ecpointformatlist != NULL); + + /* don't add extensions for SSLv3, unless doing secure renegotiation */ + if (s->version == SSL3_VERSION && !s->s3->send_connection_binding) { + return orig; + } + + ret += 2; + if (ret >= limit) { + return NULL; /* should never happen. */ + } + + if (!s->hit && s->should_ack_sni && s->session->tlsext_hostname != NULL) { + if ((long)(limit - ret - 4) < 0) { + return NULL; + } + + s2n(TLSEXT_TYPE_server_name, ret); + s2n(0, ret); + } + + if (s->s3->send_connection_binding) { + int el; + + if (!ssl_add_serverhello_renegotiate_ext(s, 0, &el, 0)) { + OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + + if ((limit - ret - 4 - el) < 0) { + return NULL; + } + + s2n(TLSEXT_TYPE_renegotiate, ret); + s2n(el, ret); + + if (!ssl_add_serverhello_renegotiate_ext(s, ret, &el, el)) { + OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + + ret += el; + } + + if (s->s3->tmp.extended_master_secret) { + if ((long)(limit - ret - 4) < 0) { + return NULL; + } + + s2n(TLSEXT_TYPE_extended_master_secret, ret); + s2n(0, ret); + } + + if (using_ecc) { + const uint8_t *plist; + size_t plistlen; + /* Add TLS extension ECPointFormats to the ServerHello message */ + long lenmax; + + tls1_get_formatlist(s, &plist, &plistlen); + + lenmax = limit - ret - 5; + if (lenmax < 0) { + return NULL; + } + if (plistlen > (size_t)lenmax) { + return NULL; + } + if (plistlen > 255) { + OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + + s2n(TLSEXT_TYPE_ec_point_formats, ret); + s2n(plistlen + 1, ret); + *(ret++) = (uint8_t)plistlen; + memcpy(ret, plist, plistlen); + ret += plistlen; + } + /* Currently the server should not respond with a SupportedCurves extension */ + + if (s->tlsext_ticket_expected && !(SSL_get_options(s) & SSL_OP_NO_TICKET)) { + if ((long)(limit - ret - 4) < 0) { + return NULL; + } + s2n(TLSEXT_TYPE_session_ticket, ret); + s2n(0, ret); + } + + if (s->s3->tmp.certificate_status_expected) { + if ((long)(limit - ret - 4) < 0) { + return NULL; + } + s2n(TLSEXT_TYPE_status_request, ret); + s2n(0, ret); + } + + if (s->srtp_profile) { + int el; + + ssl_add_serverhello_use_srtp_ext(s, 0, &el, 0); + + if ((limit - ret - 4 - el) < 0) { + return NULL; + } + + s2n(TLSEXT_TYPE_use_srtp, ret); + s2n(el, ret); + + if (!ssl_add_serverhello_use_srtp_ext(s, ret, &el, el)) { + OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, ERR_R_INTERNAL_ERROR); + return NULL; + } + ret += el; + } + + next_proto_neg_seen = s->s3->next_proto_neg_seen; + s->s3->next_proto_neg_seen = 0; + if (next_proto_neg_seen && s->ctx->next_protos_advertised_cb) { + const uint8_t *npa; + unsigned int npalen; + int r; + + r = s->ctx->next_protos_advertised_cb( + s, &npa, &npalen, s->ctx->next_protos_advertised_cb_arg); + if (r == SSL_TLSEXT_ERR_OK) { + if ((long)(limit - ret - 4 - npalen) < 0) { + return NULL; + } + s2n(TLSEXT_TYPE_next_proto_neg, ret); + s2n(npalen, ret); + memcpy(ret, npa, npalen); + ret += npalen; + s->s3->next_proto_neg_seen = 1; + } + } + + if (s->s3->alpn_selected) { + const uint8_t *selected = s->s3->alpn_selected; + size_t len = s->s3->alpn_selected_len; + + if ((long)(limit - ret - 4 - 2 - 1 - len) < 0) { + return NULL; + } + s2n(TLSEXT_TYPE_application_layer_protocol_negotiation, ret); + s2n(3 + len, ret); + s2n(1 + len, ret); + *ret++ = len; + memcpy(ret, selected, len); + ret += len; + } + + /* If the client advertised support for Channel ID, and we have it + * enabled, then we want to echo it back. */ + if (s->s3->tlsext_channel_id_valid) { + if (limit - ret - 4 < 0) { + return NULL; + } + if (s->s3->tlsext_channel_id_new) { + s2n(TLSEXT_TYPE_channel_id_new, ret); + } else { + s2n(TLSEXT_TYPE_channel_id, ret); + } + s2n(0, ret); + } + + extdatalen = ret - orig - 2; + if (extdatalen == 0) { + return orig; + } + + s2n(extdatalen, orig); + return ret; +} + +/* tls1_alpn_handle_client_hello is called to process the ALPN extension in a + * ClientHello. + * cbs: the contents of the extension, not including the type and length. + * out_alert: a pointer to the alert value to send in the event of a zero + * return. + * + * returns: 1 on success. */ +static int tls1_alpn_handle_client_hello(SSL *s, CBS *cbs, int *out_alert) { + CBS protocol_name_list, protocol_name_list_copy; + const uint8_t *selected; + uint8_t selected_len; + int r; + + if (s->ctx->alpn_select_cb == NULL) { + return 1; + } + + if (!CBS_get_u16_length_prefixed(cbs, &protocol_name_list) || + CBS_len(cbs) != 0 || CBS_len(&protocol_name_list) < 2) { + goto parse_error; + } + + /* Validate the protocol list. */ + 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)) { + goto parse_error; + } + } + + r = s->ctx->alpn_select_cb( + s, &selected, &selected_len, CBS_data(&protocol_name_list), + CBS_len(&protocol_name_list), s->ctx->alpn_select_cb_arg); + if (r == SSL_TLSEXT_ERR_OK) { + if (s->s3->alpn_selected) { + OPENSSL_free(s->s3->alpn_selected); + } + s->s3->alpn_selected = BUF_memdup(selected, selected_len); + if (!s->s3->alpn_selected) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + s->s3->alpn_selected_len = selected_len; + } + + return 1; + +parse_error: + *out_alert = SSL_AD_DECODE_ERROR; + return 0; +} + +static int ssl_scan_clienthello_tlsext(SSL *s, CBS *cbs, int *out_alert) { + int renegotiate_seen = 0; + CBS extensions; + + s->should_ack_sni = 0; + s->srtp_profile = NULL; + s->s3->next_proto_neg_seen = 0; + s->s3->tmp.certificate_status_expected = 0; + s->s3->tmp.extended_master_secret = 0; + + if (s->s3->alpn_selected) { + OPENSSL_free(s->s3->alpn_selected); + s->s3->alpn_selected = NULL; + } + + /* Clear any signature algorithms extension received */ + if (s->cert->peer_sigalgs) { + OPENSSL_free(s->cert->peer_sigalgs); + s->cert->peer_sigalgs = NULL; + } + + /* Clear any shared signature algorithms */ + if (s->cert->shared_sigalgs) { + OPENSSL_free(s->cert->shared_sigalgs); + s->cert->shared_sigalgs = NULL; + } + + /* Clear ECC extensions */ + if (s->s3->tmp.peer_ecpointformatlist != 0) { + OPENSSL_free(s->s3->tmp.peer_ecpointformatlist); + s->s3->tmp.peer_ecpointformatlist = NULL; + s->s3->tmp.peer_ecpointformatlist_length = 0; + } + + if (s->s3->tmp.peer_ellipticcurvelist != 0) { + OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist); + s->s3->tmp.peer_ellipticcurvelist = NULL; + s->s3->tmp.peer_ellipticcurvelist_length = 0; + } + + /* There may be no extensions. */ + if (CBS_len(cbs) == 0) { + goto ri_check; + } + + /* Decode the extensions block and check it is valid. */ + if (!CBS_get_u16_length_prefixed(cbs, &extensions) || + !tls1_check_duplicate_extensions(&extensions)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 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; + } + + if (s->tlsext_debug_cb) { + s->tlsext_debug_cb(s, 0, type, (uint8_t *)CBS_data(&extension), + CBS_len(&extension), s->tlsext_debug_arg); + } + + /* The servername extension is treated as follows: + + - Only the hostname type is supported with a maximum length of 255. + - The servername is rejected if too long or if it contains zeros, in + which case an fatal alert is generated. + - The servername field is maintained together with the session cache. + - When a session is resumed, the servername call back invoked in order + to allow the application to position itself to the right context. + - The servername is acknowledged if it is new for a session or when + it is identical to a previously used for the same session. + Applications can control the behaviour. They can at any time + set a 'desirable' servername for a new SSL object. This can be the + case for example with HTTPS when a Host: header field is received and + a renegotiation is requested. In this case, a possible servername + presented in the new client hello is only acknowledged if it matches + the value of the Host: field. + - Applications must use SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + if they provide for changing an explicit servername context for the + session, + i.e. when the session has been established with a servername extension. + - On session reconnect, the servername extension may be absent. */ + + if (type == TLSEXT_TYPE_server_name) { + CBS server_name_list; + char have_seen_host_name = 0; + + if (!CBS_get_u16_length_prefixed(&extension, &server_name_list) || + CBS_len(&server_name_list) < 1 || CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Decode each ServerName in the extension. */ + while (CBS_len(&server_name_list) > 0) { + uint8_t name_type; + CBS host_name; + + /* Decode the NameType. */ + if (!CBS_get_u8(&server_name_list, &name_type)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Only host_name is supported. */ + if (name_type != TLSEXT_NAMETYPE_host_name) { + continue; + } + + if (have_seen_host_name) { + /* The ServerNameList MUST NOT contain more than one name of the same + * name_type. */ + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + have_seen_host_name = 1; + + if (!CBS_get_u16_length_prefixed(&server_name_list, &host_name) || + CBS_len(&host_name) < 1) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (CBS_len(&host_name) > TLSEXT_MAXLEN_host_name || + CBS_contains_zero_byte(&host_name)) { + *out_alert = SSL_AD_UNRECOGNIZED_NAME; + return 0; + } + + if (!s->hit) { + assert(s->session->tlsext_hostname == NULL); + if (s->session->tlsext_hostname) { + /* This should be impossible. */ + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Copy the hostname as a string. */ + if (!CBS_strdup(&host_name, &s->session->tlsext_hostname)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + s->should_ack_sni = 1; + } + } + } else if (type == TLSEXT_TYPE_ec_point_formats) { + CBS ec_point_format_list; + + if (!CBS_get_u8_length_prefixed(&extension, &ec_point_format_list) || + CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (!CBS_stow(&ec_point_format_list, &s->s3->tmp.peer_ecpointformatlist, + &s->s3->tmp.peer_ecpointformatlist_length)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + } else if (type == TLSEXT_TYPE_elliptic_curves) { + CBS elliptic_curve_list; + size_t i, num_curves; + + if (!CBS_get_u16_length_prefixed(&extension, &elliptic_curve_list) || + CBS_len(&elliptic_curve_list) == 0 || + (CBS_len(&elliptic_curve_list) & 1) != 0 || + CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (s->s3->tmp.peer_ellipticcurvelist) { + OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist); + s->s3->tmp.peer_ellipticcurvelist_length = 0; + } + + s->s3->tmp.peer_ellipticcurvelist = + (uint16_t *)OPENSSL_malloc(CBS_len(&elliptic_curve_list)); + + if (s->s3->tmp.peer_ellipticcurvelist == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + num_curves = CBS_len(&elliptic_curve_list) / 2; + for (i = 0; i < num_curves; i++) { + if (!CBS_get_u16(&elliptic_curve_list, + &s->s3->tmp.peer_ellipticcurvelist[i])) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + } + + if (CBS_len(&elliptic_curve_list) != 0) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + s->s3->tmp.peer_ellipticcurvelist_length = num_curves; + } else if (type == TLSEXT_TYPE_renegotiate) { + if (!ssl_parse_clienthello_renegotiate_ext(s, &extension, out_alert)) { + return 0; + } + renegotiate_seen = 1; + } else if (type == TLSEXT_TYPE_signature_algorithms) { + CBS supported_signature_algorithms; + + if (!CBS_get_u16_length_prefixed(&extension, + &supported_signature_algorithms) || + CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Ensure the signature algorithms are non-empty. It contains a list of + * SignatureAndHashAlgorithms which are two bytes each. */ + if (CBS_len(&supported_signature_algorithms) == 0 || + (CBS_len(&supported_signature_algorithms) % 2) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (!tls1_process_sigalgs(s, &supported_signature_algorithms)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + /* If sigalgs received and no shared algorithms fatal error. */ + if (s->cert->peer_sigalgs && !s->cert->shared_sigalgs) { + OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_tlsext, + SSL_R_NO_SHARED_SIGATURE_ALGORITHMS); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + } else if (type == TLSEXT_TYPE_next_proto_neg && + s->s3->tmp.finish_md_len == 0 && s->s3->alpn_selected == NULL && + !SSL_IS_DTLS(s)) { + /* The extension must be empty. */ + if (CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* We shouldn't accept this extension on a renegotiation. + * + * s->new_session will be set on renegotiation, but we probably shouldn't + * rely that it couldn't be set on the initial renegotation too in + * certain cases (when there's some other reason to disallow resuming an + * earlier session -- the current code won't be doing anything like that, + * but this might change). + + * A valid sign that there's been a previous handshake in this connection + * is if s->s3->tmp.finish_md_len > 0. (We are talking about a check + * that will happen in the Hello protocol round, well before a new + * Finished message could have been computed.) */ + s->s3->next_proto_neg_seen = 1; + } else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation && + s->ctx->alpn_select_cb && s->s3->tmp.finish_md_len == 0) { + if (!tls1_alpn_handle_client_hello(s, &extension, out_alert)) { + return 0; + } + /* ALPN takes precedence over NPN. */ + s->s3->next_proto_neg_seen = 0; + } else if (type == TLSEXT_TYPE_channel_id && s->tlsext_channel_id_enabled && + !SSL_IS_DTLS(s)) { + /* The extension must be empty. */ + if (CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + s->s3->tlsext_channel_id_valid = 1; + } else if (type == TLSEXT_TYPE_channel_id_new && + s->tlsext_channel_id_enabled && !SSL_IS_DTLS(s)) { + /* The extension must be empty. */ + if (CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + s->s3->tlsext_channel_id_valid = 1; + s->s3->tlsext_channel_id_new = 1; + } else if (type == TLSEXT_TYPE_use_srtp) { + if (!ssl_parse_clienthello_use_srtp_ext(s, &extension, out_alert)) { + return 0; + } + } else if (type == TLSEXT_TYPE_extended_master_secret && + s->version != SSL3_VERSION) { + if (CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + s->s3->tmp.extended_master_secret = 1; + } + } + +ri_check: + /* Need RI if renegotiating */ + + if (!renegotiate_seen && s->renegotiate && + !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) { + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl_scan_clienthello_tlsext, + SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED); + return 0; + } + + return 1; +} + +int ssl_parse_clienthello_tlsext(SSL *s, CBS *cbs) { + int alert = -1; + if (ssl_scan_clienthello_tlsext(s, cbs, &alert) <= 0) { + ssl3_send_alert(s, SSL3_AL_FATAL, alert); + return 0; + } + + if (ssl_check_clienthello_tlsext(s) <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_tlsext, + SSL_R_CLIENTHELLO_TLSEXT); + return 0; + } + + return 1; +} + +/* ssl_next_proto_validate validates a Next Protocol Negotiation block. No + * elements of zero length are allowed and the set of elements must exactly + * fill the length of the block. */ +static char ssl_next_proto_validate(const CBS *cbs) { + CBS copy = *cbs; + + while (CBS_len(©) != 0) { + CBS proto; + if (!CBS_get_u8_length_prefixed(©, &proto) || CBS_len(&proto) == 0) { + return 0; + } + } + + return 1; +} + +static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert) { + int tlsext_servername = 0; + int renegotiate_seen = 0; + CBS extensions; + + /* TODO(davidben): Move all of these to some per-handshake state that gets + * systematically reset on a new handshake; perhaps allocate it fresh each + * time so it's not even kept around post-handshake. */ + s->s3->next_proto_neg_seen = 0; + s->tlsext_ticket_expected = 0; + s->s3->tmp.certificate_status_expected = 0; + s->s3->tmp.extended_master_secret = 0; + s->srtp_profile = NULL; + + if (s->s3->alpn_selected) { + OPENSSL_free(s->s3->alpn_selected); + s->s3->alpn_selected = NULL; + } + + /* Clear ECC extensions */ + if (s->s3->tmp.peer_ecpointformatlist != 0) { + OPENSSL_free(s->s3->tmp.peer_ecpointformatlist); + s->s3->tmp.peer_ecpointformatlist = NULL; + s->s3->tmp.peer_ecpointformatlist_length = 0; + } + + /* There may be no extensions. */ + if (CBS_len(cbs) == 0) { + goto ri_check; + } + + /* Decode the extensions block and check it is valid. */ + if (!CBS_get_u16_length_prefixed(cbs, &extensions) || + !tls1_check_duplicate_extensions(&extensions)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 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; + } + + if (s->tlsext_debug_cb) { + s->tlsext_debug_cb(s, 1, type, (uint8_t *)CBS_data(&extension), + CBS_len(&extension), s->tlsext_debug_arg); + } + + if (type == TLSEXT_TYPE_server_name) { + /* The extension must be empty. */ + if (CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* We must have sent it in ClientHello. */ + if (s->tlsext_hostname == NULL) { + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + tlsext_servername = 1; + } else if (type == TLSEXT_TYPE_ec_point_formats) { + CBS ec_point_format_list; + + if (!CBS_get_u8_length_prefixed(&extension, &ec_point_format_list) || + CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (!CBS_stow(&ec_point_format_list, &s->s3->tmp.peer_ecpointformatlist, + &s->s3->tmp.peer_ecpointformatlist_length)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + } else if (type == TLSEXT_TYPE_session_ticket) { + if ((SSL_get_options(s) & SSL_OP_NO_TICKET) || CBS_len(&extension) > 0) { + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + s->tlsext_ticket_expected = 1; + } else if (type == TLSEXT_TYPE_status_request) { + /* The extension MUST be empty and may only sent if we've requested a + * status request message. */ + if (CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (!s->ocsp_stapling_enabled) { + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + /* Set a flag to expect a CertificateStatus message */ + s->s3->tmp.certificate_status_expected = 1; + } else if (type == TLSEXT_TYPE_next_proto_neg && + s->s3->tmp.finish_md_len == 0 && + !SSL_IS_DTLS(s)) { + uint8_t *selected; + uint8_t selected_len; + + /* We must have requested it. */ + if (s->ctx->next_proto_select_cb == NULL) { + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + /* The data must be valid. */ + if (!ssl_next_proto_validate(&extension)) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (s->ctx->next_proto_select_cb( + s, &selected, &selected_len, CBS_data(&extension), + CBS_len(&extension), + s->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + s->next_proto_negotiated = BUF_memdup(selected, selected_len); + if (s->next_proto_negotiated == NULL) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + + s->next_proto_negotiated_len = selected_len; + s->s3->next_proto_neg_seen = 1; + } else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation) { + CBS protocol_name_list, protocol_name; + + /* We must have requested it. */ + if (s->alpn_client_proto_list == NULL) { + *out_alert = SSL_AD_UNSUPPORTED_EXTENSION; + return 0; + } + + /* The extension data consists of a ProtocolNameList which must have + * exactly one ProtocolName. Each of these is length-prefixed. */ + if (!CBS_get_u16_length_prefixed(&extension, &protocol_name_list) || + CBS_len(&extension) != 0 || + !CBS_get_u8_length_prefixed(&protocol_name_list, &protocol_name) || + CBS_len(&protocol_name_list) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + if (!CBS_stow(&protocol_name, &s->s3->alpn_selected, + &s->s3->alpn_selected_len)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + } else if (type == TLSEXT_TYPE_channel_id && !SSL_IS_DTLS(s)) { + if (CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + s->s3->tlsext_channel_id_valid = 1; + } else if (type == TLSEXT_TYPE_channel_id_new && !SSL_IS_DTLS(s)) { + if (CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + s->s3->tlsext_channel_id_valid = 1; + s->s3->tlsext_channel_id_new = 1; + } else if (type == TLSEXT_TYPE_certificate_timestamp) { + if (CBS_len(&extension) == 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Session resumption uses the original session information. */ + if (!s->hit && + !CBS_stow(&extension, &s->session->tlsext_signed_cert_timestamp_list, + &s->session->tlsext_signed_cert_timestamp_list_length)) { + *out_alert = SSL_AD_INTERNAL_ERROR; + return 0; + } + } else if (type == TLSEXT_TYPE_renegotiate) { + if (!ssl_parse_serverhello_renegotiate_ext(s, &extension, out_alert)) { + return 0; + } + + renegotiate_seen = 1; + } else if (type == TLSEXT_TYPE_use_srtp) { + if (!ssl_parse_serverhello_use_srtp_ext(s, &extension, out_alert)) { + return 0; + } + } else if (type == TLSEXT_TYPE_extended_master_secret) { + if (/* It is invalid for the server to select EMS and + SSLv3. */ + s->version == SSL3_VERSION || CBS_len(&extension) != 0) { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + s->s3->tmp.extended_master_secret = 1; + } + } + + if (!s->hit && tlsext_servername == 1 && s->tlsext_hostname) { + if (s->session->tlsext_hostname == NULL) { + s->session->tlsext_hostname = BUF_strdup(s->tlsext_hostname); + if (!s->session->tlsext_hostname) { + *out_alert = SSL_AD_UNRECOGNIZED_NAME; + return 0; + } + } else { + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + } + +ri_check: + /* Determine if we need to see RI. Strictly speaking if we want to avoid an + * attack we should *always* see RI even on initial server hello 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 so for + * the immediate future tolerate RI absence on initial connect only. */ + if (!renegotiate_seen && !(s->options & SSL_OP_LEGACY_SERVER_CONNECT) && + !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) { + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + OPENSSL_PUT_ERROR(SSL, ssl_scan_serverhello_tlsext, + SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED); + return 0; + } + + return 1; +} + +int ssl_prepare_clienthello_tlsext(SSL *s) { return 1; } + +int ssl_prepare_serverhello_tlsext(SSL *s) { return 1; } + +static int ssl_check_clienthello_tlsext(SSL *s) { + int ret = SSL_TLSEXT_ERR_NOACK; + int al = SSL_AD_UNRECOGNIZED_NAME; + + /* The handling of the ECPointFormats extension is done elsewhere, namely in + * ssl3_choose_cipher in s3_lib.c. */ + + if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0) { + ret = s->ctx->tlsext_servername_callback(s, &al, + s->ctx->tlsext_servername_arg); + } else if (s->initial_ctx != NULL && + s->initial_ctx->tlsext_servername_callback != 0) { + ret = s->initial_ctx->tlsext_servername_callback( + s, &al, s->initial_ctx->tlsext_servername_arg); + } + + switch (ret) { + case SSL_TLSEXT_ERR_ALERT_FATAL: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + return -1; + + case SSL_TLSEXT_ERR_ALERT_WARNING: + ssl3_send_alert(s, SSL3_AL_WARNING, al); + return 1; + + case SSL_TLSEXT_ERR_NOACK: + s->should_ack_sni = 0; + return 1; + + default: + return 1; + } +} + +static int ssl_check_serverhello_tlsext(SSL *s) { + int ret = SSL_TLSEXT_ERR_NOACK; + int al = SSL_AD_UNRECOGNIZED_NAME; + + /* If we are client and using an elliptic curve cryptography cipher suite, + * then if server returns an EC point formats lists extension it must contain + * uncompressed. */ + unsigned long alg_k = s->s3->tmp.new_cipher->algorithm_mkey; + unsigned long alg_a = s->s3->tmp.new_cipher->algorithm_auth; + if (((alg_k & SSL_kEECDH) || (alg_a & SSL_aECDSA)) && + !tls1_check_point_format(s, TLSEXT_ECPOINTFORMAT_uncompressed)) { + OPENSSL_PUT_ERROR(SSL, ssl_check_serverhello_tlsext, + SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST); + return -1; + } + ret = SSL_TLSEXT_ERR_OK; + + if (s->ctx != NULL && s->ctx->tlsext_servername_callback != 0) { + ret = s->ctx->tlsext_servername_callback(s, &al, + s->ctx->tlsext_servername_arg); + } else if (s->initial_ctx != NULL && + s->initial_ctx->tlsext_servername_callback != 0) { + ret = s->initial_ctx->tlsext_servername_callback( + s, &al, s->initial_ctx->tlsext_servername_arg); + } + + switch (ret) { + case SSL_TLSEXT_ERR_ALERT_FATAL: + ssl3_send_alert(s, SSL3_AL_FATAL, al); + return -1; + + case SSL_TLSEXT_ERR_ALERT_WARNING: + ssl3_send_alert(s, SSL3_AL_WARNING, al); + return 1; + + default: + return 1; + } +} + +int ssl_parse_serverhello_tlsext(SSL *s, CBS *cbs) { + int alert = -1; + if (s->version < SSL3_VERSION) { + return 1; + } + + if (ssl_scan_serverhello_tlsext(s, cbs, &alert) <= 0) { + ssl3_send_alert(s, SSL3_AL_FATAL, alert); + return 0; + } + + if (ssl_check_serverhello_tlsext(s) <= 0) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_tlsext, + SSL_R_SERVERHELLO_TLSEXT); + return 0; + } + + return 1; +} + +/* Since the server cache lookup is done early on in the processing of the + * ClientHello, and other operations depend on the result, we need to handle + * any TLS session ticket extension at the same time. + * + * ctx: contains the early callback context, which is the result of a + * shallow parse of the ClientHello. + * ret: (output) on return, if a ticket was decrypted, then this is set to + * point to the resulting session. + * + * Returns: + * -1: fatal error, either from parsing or decrypting the ticket. + * 0: no ticket was found (or was ignored, based on settings). + * 1: a zero length extension was found, indicating that the client supports + * session tickets but doesn't currently have one to offer. + * 2: a ticket was offered but couldn't be decrypted because of a non-fatal + * error. + * 3: a ticket was successfully decrypted and *ret was set. + * + * Side effects: + * Sets s->tlsext_ticket_expected to 1 if the server will have to issue + * a new session ticket to the client because the client indicated support + * but the client either doesn't have a session ticket or we couldn't use + * the one it gave us, or if s->ctx->tlsext_ticket_key_cb asked to renew + * the client's ticket. Otherwise, s->tlsext_ticket_expected is set to 0. + */ +int tls1_process_ticket(SSL *s, const struct ssl_early_callback_ctx *ctx, + SSL_SESSION **ret) { + *ret = NULL; + s->tlsext_ticket_expected = 0; + const uint8_t *data; + size_t len; + int r; + + /* If tickets disabled behave as if no ticket present to permit stateful + * resumption. */ + if ((SSL_get_options(s) & SSL_OP_NO_TICKET) || + (s->version <= SSL3_VERSION && !ctx->extensions) || + !SSL_early_callback_ctx_extension_get(ctx, TLSEXT_TYPE_session_ticket, + &data, &len)) { + return 0; + } + + if (len == 0) { + /* The client will accept a ticket but doesn't currently have one. */ + s->tlsext_ticket_expected = 1; + return 1; + } + + r = tls_decrypt_ticket(s, data, len, ctx->session_id, ctx->session_id_len, + ret); + switch (r) { + case 2: /* ticket couldn't be decrypted */ + s->tlsext_ticket_expected = 1; + return 2; + + case 3: /* ticket was decrypted */ + return r; + + case 4: /* ticket decrypted but need to renew */ + s->tlsext_ticket_expected = 1; + return 3; + + default: /* fatal error */ + return -1; + } +} + +/* tls_decrypt_ticket attempts to decrypt a session ticket. + * + * etick: points to the body of the session ticket extension. + * eticklen: the length of the session tickets extenion. + * sess_id: points at the session ID. + * sesslen: the length of the session ID. + * psess: (output) on return, if a ticket was decrypted, then this is set to + * point to the resulting session. + * + * Returns: + * -1: fatal error, either from parsing or decrypting the ticket. + * 2: the ticket couldn't be decrypted. + * 3: a ticket was successfully decrypted and *psess was set. + * 4: same as 3, but the ticket needs to be renewed. */ +static int tls_decrypt_ticket(SSL *s, const uint8_t *etick, int eticklen, + const uint8_t *sess_id, int sesslen, + SSL_SESSION **psess) { + SSL_SESSION *sess; + uint8_t *sdec; + const uint8_t *p; + int slen, mlen, renew_ticket = 0; + uint8_t tick_hmac[EVP_MAX_MD_SIZE]; + HMAC_CTX hctx; + EVP_CIPHER_CTX ctx; + SSL_CTX *tctx = s->initial_ctx; + + /* Need at least keyname + iv + some encrypted data */ + if (eticklen < 48) { + return 2; + } + + /* Initialize session ticket encryption and HMAC contexts */ + HMAC_CTX_init(&hctx); + EVP_CIPHER_CTX_init(&ctx); + if (tctx->tlsext_ticket_key_cb) { + uint8_t *nctick = (uint8_t *)etick; + int rv = tctx->tlsext_ticket_key_cb(s, nctick, nctick + 16, &ctx, &hctx, 0); + if (rv < 0) { + return -1; + } + if (rv == 0) { + return 2; + } + if (rv == 2) { + renew_ticket = 1; + } + } else { + /* Check key name matches */ + if (memcmp(etick, tctx->tlsext_tick_key_name, 16)) { + return 2; + } + if (!HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(), + NULL) || + !EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, + tctx->tlsext_tick_aes_key, etick + 16)) { + HMAC_CTX_cleanup(&hctx); + EVP_CIPHER_CTX_cleanup(&ctx); + return -1; + } + } + + /* Attempt to process session ticket, first conduct sanity and integrity + * checks on ticket. */ + mlen = HMAC_size(&hctx); + if (mlen < 0) { + HMAC_CTX_cleanup(&hctx); + EVP_CIPHER_CTX_cleanup(&ctx); + return -1; + } + eticklen -= mlen; + /* Check HMAC of encrypted ticket */ + HMAC_Update(&hctx, etick, eticklen); + HMAC_Final(&hctx, tick_hmac, NULL); + HMAC_CTX_cleanup(&hctx); + if (CRYPTO_memcmp(tick_hmac, etick + eticklen, mlen)) { + EVP_CIPHER_CTX_cleanup(&ctx); + return 2; + } + + /* Attempt to decrypt session data */ + /* Move p after IV to start of encrypted ticket, update length */ + p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx); + eticklen -= 16 + EVP_CIPHER_CTX_iv_length(&ctx); + sdec = OPENSSL_malloc(eticklen); + if (!sdec) { + EVP_CIPHER_CTX_cleanup(&ctx); + return -1; + } + EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen); + if (EVP_DecryptFinal_ex(&ctx, sdec + slen, &mlen) <= 0) { + EVP_CIPHER_CTX_cleanup(&ctx); + OPENSSL_free(sdec); + return 2; + } + slen += mlen; + EVP_CIPHER_CTX_cleanup(&ctx); + p = sdec; + + sess = d2i_SSL_SESSION(NULL, &p, slen); + OPENSSL_free(sdec); + if (sess) { + /* The session ID, if non-empty, is used by some clients to detect that the + * ticket has been accepted. So we copy it to the session structure. If it + * is empty set length to zero as required by standard. */ + if (sesslen) { + memcpy(sess->session_id, sess_id, sesslen); + } + sess->session_id_length = sesslen; + *psess = sess; + if (renew_ticket) { + return 4; + } + return 3; + } + + ERR_clear_error(); + /* For session parse failure, indicate that we need to send a new ticket. */ + return 2; +} + +/* Tables to translate from NIDs to TLS v1.2 ids */ +typedef struct { + int nid; + int id; +} tls12_lookup; + +static const tls12_lookup tls12_md[] = {{NID_md5, TLSEXT_hash_md5}, + {NID_sha1, TLSEXT_hash_sha1}, + {NID_sha224, TLSEXT_hash_sha224}, + {NID_sha256, TLSEXT_hash_sha256}, + {NID_sha384, TLSEXT_hash_sha384}, + {NID_sha512, TLSEXT_hash_sha512}}; + +static const tls12_lookup tls12_sig[] = {{EVP_PKEY_RSA, TLSEXT_signature_rsa}, + {EVP_PKEY_EC, TLSEXT_signature_ecdsa}}; + +static int tls12_find_id(int nid, const tls12_lookup *table, size_t tlen) { + size_t i; + for (i = 0; i < tlen; i++) { + if (table[i].nid == nid) { + return table[i].id; + } + } + + return -1; +} + +static int tls12_find_nid(int id, const tls12_lookup *table, size_t tlen) { + size_t i; + for (i = 0; i < tlen; i++) { + if (table[i].id == id) { + return table[i].nid; + } + } + + return NID_undef; +} + +int tls12_get_sigandhash(uint8_t *p, const EVP_PKEY *pk, const EVP_MD *md) { + int sig_id, md_id; + + if (!md) { + return 0; + } + + md_id = tls12_find_id(EVP_MD_type(md), tls12_md, + sizeof(tls12_md) / sizeof(tls12_lookup)); + if (md_id == -1) { + return 0; + } + + sig_id = tls12_get_sigid(pk); + if (sig_id == -1) { + return 0; + } + + p[0] = (uint8_t)md_id; + p[1] = (uint8_t)sig_id; + return 1; +} + +int tls12_get_sigid(const EVP_PKEY *pk) { + return tls12_find_id(pk->type, tls12_sig, + sizeof(tls12_sig) / sizeof(tls12_lookup)); +} + +const EVP_MD *tls12_get_hash(uint8_t hash_alg) { + switch (hash_alg) { + case TLSEXT_hash_md5: + return EVP_md5(); + + case TLSEXT_hash_sha1: + return EVP_sha1(); + + case TLSEXT_hash_sha224: + return EVP_sha224(); + + case TLSEXT_hash_sha256: + return EVP_sha256(); + + case TLSEXT_hash_sha384: + return EVP_sha384(); + + case TLSEXT_hash_sha512: + return EVP_sha512(); + + default: + return NULL; + } +} + +/* tls12_get_pkey_type returns the EVP_PKEY type corresponding to TLS signature + * algorithm |sig_alg|. It returns -1 if the type is unknown. */ +static int tls12_get_pkey_type(uint8_t sig_alg) { + switch (sig_alg) { + case TLSEXT_signature_rsa: + return EVP_PKEY_RSA; + + case TLSEXT_signature_ecdsa: + return EVP_PKEY_EC; + + default: + return -1; + } +} + +/* Convert TLS 1.2 signature algorithm extension values into NIDs */ +static void tls1_lookup_sigalg(int *phash_nid, int *psign_nid, + int *psignhash_nid, const uint8_t *data) { + int sign_nid = 0, hash_nid = 0; + if (!phash_nid && !psign_nid && !psignhash_nid) { + return; + } + + if (phash_nid || psignhash_nid) { + hash_nid = tls12_find_nid(data[0], tls12_md, + sizeof(tls12_md) / sizeof(tls12_lookup)); + if (phash_nid) { + *phash_nid = hash_nid; + } + } + + if (psign_nid || psignhash_nid) { + sign_nid = tls12_find_nid(data[1], tls12_sig, + sizeof(tls12_sig) / sizeof(tls12_lookup)); + if (psign_nid) { + *psign_nid = sign_nid; + } + } + + if (psignhash_nid) { + if (sign_nid && hash_nid) { + OBJ_find_sigid_by_algs(psignhash_nid, hash_nid, sign_nid); + } else { + *psignhash_nid = NID_undef; + } + } +} + +/* Given preference and allowed sigalgs set shared sigalgs */ +static int tls12_do_shared_sigalgs(TLS_SIGALGS *shsig, const uint8_t *pref, + size_t preflen, const uint8_t *allow, + size_t allowlen) { + const uint8_t *ptmp, *atmp; + size_t i, j, nmatch = 0; + + for (i = 0, ptmp = pref; i < preflen; i += 2, ptmp += 2) { + /* Skip disabled hashes or signature algorithms */ + if (tls12_get_hash(ptmp[0]) == NULL || + tls12_get_pkey_type(ptmp[1]) == -1) { + continue; + } + + for (j = 0, atmp = allow; j < allowlen; j += 2, atmp += 2) { + if (ptmp[0] == atmp[0] && ptmp[1] == atmp[1]) { + nmatch++; + if (shsig) { + shsig->rhash = ptmp[0]; + shsig->rsign = ptmp[1]; + tls1_lookup_sigalg(&shsig->hash_nid, &shsig->sign_nid, + &shsig->signandhash_nid, ptmp); + shsig++; + } + + break; + } + } + } + + return nmatch; +} + +/* Set shared signature algorithms for SSL structures */ +static int tls1_set_shared_sigalgs(SSL *s) { + const uint8_t *pref, *allow, *conf; + size_t preflen, allowlen, conflen; + size_t nmatch; + TLS_SIGALGS *salgs = NULL; + CERT *c = s->cert; + + if (c->shared_sigalgs) { + OPENSSL_free(c->shared_sigalgs); + c->shared_sigalgs = NULL; + } + + /* If client use client signature algorithms if not NULL */ + if (!s->server && c->client_sigalgs) { + conf = c->client_sigalgs; + conflen = c->client_sigalgslen; + } else if (c->conf_sigalgs) { + conf = c->conf_sigalgs; + conflen = c->conf_sigalgslen; + } else { + conflen = tls12_get_psigalgs(s, &conf); + } + + if (s->options & SSL_OP_CIPHER_SERVER_PREFERENCE) { + pref = conf; + preflen = conflen; + allow = c->peer_sigalgs; + allowlen = c->peer_sigalgslen; + } else { + allow = conf; + allowlen = conflen; + pref = c->peer_sigalgs; + preflen = c->peer_sigalgslen; + } + + nmatch = tls12_do_shared_sigalgs(NULL, pref, preflen, allow, allowlen); + if (!nmatch) { + return 1; + } + + salgs = OPENSSL_malloc(nmatch * sizeof(TLS_SIGALGS)); + if (!salgs) { + return 0; + } + + nmatch = tls12_do_shared_sigalgs(salgs, pref, preflen, allow, allowlen); + c->shared_sigalgs = salgs; + c->shared_sigalgslen = nmatch; + return 1; +} + +/* Set preferred digest for each key type */ +int tls1_process_sigalgs(SSL *s, const CBS *sigalgs) { + CERT *c = s->cert; + + /* Extension ignored for inappropriate versions */ + if (!SSL_USE_SIGALGS(s)) { + return 1; + } + + /* Length must be even */ + if (CBS_len(sigalgs) % 2 != 0) { + return 0; + } + + /* Should never happen */ + if (!c) { + return 0; + } + + if (!CBS_stow(sigalgs, &c->peer_sigalgs, &c->peer_sigalgslen)) { + return 0; + } + + tls1_set_shared_sigalgs(s); + return 1; +} + +const EVP_MD *tls1_choose_signing_digest(SSL *s, EVP_PKEY *pkey) { + CERT *c = s->cert; + int type = EVP_PKEY_id(pkey); + size_t i; + + /* Select the first shared digest supported by our key. */ + for (i = 0; i < c->shared_sigalgslen; i++) { + const EVP_MD *md = tls12_get_hash(c->shared_sigalgs[i].rhash); + if (md == NULL || + tls12_get_pkey_type(c->shared_sigalgs[i].rsign) != type || + !EVP_PKEY_supports_digest(pkey, md)) { + continue; + } + return md; + } + + /* If no suitable digest may be found, default to SHA-1. */ + return EVP_sha1(); +} + +int SSL_get_sigalgs(SSL *s, int idx, int *psign, int *phash, int *psignhash, + uint8_t *rsig, uint8_t *rhash) { + const uint8_t *psig = s->cert->peer_sigalgs; + + if (psig == NULL) { + return 0; + } + + if (idx >= 0) { + idx <<= 1; + if (idx >= (int)s->cert->peer_sigalgslen) { + return 0; + } + psig += idx; + if (rhash) { + *rhash = psig[0]; + } + if (rsig) { + *rsig = psig[1]; + } + tls1_lookup_sigalg(phash, psign, psignhash, psig); + } + + return s->cert->peer_sigalgslen / 2; +} + +int SSL_get_shared_sigalgs(SSL *s, int idx, int *psign, int *phash, + int *psignhash, uint8_t *rsig, uint8_t *rhash) { + TLS_SIGALGS *shsigalgs = s->cert->shared_sigalgs; + + if (!shsigalgs || idx >= (int)s->cert->shared_sigalgslen) { + return 0; + } + + shsigalgs += idx; + if (phash) { + *phash = shsigalgs->hash_nid; + } + if (psign) { + *psign = shsigalgs->sign_nid; + } + if (psignhash) { + *psignhash = shsigalgs->signandhash_nid; + } + if (rsig) { + *rsig = shsigalgs->rsign; + } + if (rhash) { + *rhash = shsigalgs->rhash; + } + + return s->cert->shared_sigalgslen; +} + +/* tls1_channel_id_hash calculates the signed data for a Channel ID on the + * given SSL connection and writes it to |md|. */ +int tls1_channel_id_hash(EVP_MD_CTX *md, SSL *s) { + EVP_MD_CTX ctx; + uint8_t temp_digest[EVP_MAX_MD_SIZE]; + unsigned temp_digest_len; + int i; + static const char kClientIDMagic[] = "TLS Channel ID signature"; + + if (s->s3->handshake_buffer && + !ssl3_digest_cached_records(s, free_handshake_buffer)) { + return 0; + } + + EVP_DigestUpdate(md, kClientIDMagic, sizeof(kClientIDMagic)); + + if (s->hit && s->s3->tlsext_channel_id_new) { + static const char kResumptionMagic[] = "Resumption"; + EVP_DigestUpdate(md, kResumptionMagic, sizeof(kResumptionMagic)); + if (s->session->original_handshake_hash_len == 0) { + return 0; + } + EVP_DigestUpdate(md, s->session->original_handshake_hash, + s->session->original_handshake_hash_len); + } + + EVP_MD_CTX_init(&ctx); + for (i = 0; i < SSL_MAX_DIGEST; i++) { + if (s->s3->handshake_dgst[i] == NULL) { + continue; + } + EVP_MD_CTX_copy_ex(&ctx, s->s3->handshake_dgst[i]); + EVP_DigestFinal_ex(&ctx, temp_digest, &temp_digest_len); + EVP_DigestUpdate(md, temp_digest, temp_digest_len); + } + EVP_MD_CTX_cleanup(&ctx); + + return 1; +} + +/* tls1_record_handshake_hashes_for_channel_id records the current handshake + * hashes in |s->session| so that Channel ID resumptions can sign that data. */ +int tls1_record_handshake_hashes_for_channel_id(SSL *s) { + int digest_len; + /* 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 (s->hit) { + return -1; + } + + /* It only makes sense to call this function if Channel IDs have been + * negotiated. */ + if (!s->s3->tlsext_channel_id_new) { + return -1; + } + + digest_len = + tls1_handshake_digest(s, s->session->original_handshake_hash, + sizeof(s->session->original_handshake_hash)); + if (digest_len < 0) { + return -1; + } + + s->session->original_handshake_hash_len = digest_len; + + return 1; +} + +int tls1_set_sigalgs(CERT *c, const int *psig_nids, size_t salglen, + int client) { + uint8_t *sigalgs, *sptr; + int rhash, rsign; + size_t i; + + if (salglen & 1) { + return 0; + } + + sigalgs = OPENSSL_malloc(salglen); + if (sigalgs == NULL) { + return 0; + } + + for (i = 0, sptr = sigalgs; i < salglen; i += 2) { + rhash = tls12_find_id(*psig_nids++, tls12_md, + sizeof(tls12_md) / sizeof(tls12_lookup)); + rsign = tls12_find_id(*psig_nids++, tls12_sig, + sizeof(tls12_sig) / sizeof(tls12_lookup)); + + if (rhash == -1 || rsign == -1) { + goto err; + } + *sptr++ = rhash; + *sptr++ = rsign; + } + + if (client) { + if (c->client_sigalgs) { + OPENSSL_free(c->client_sigalgs); + } + c->client_sigalgs = sigalgs; + c->client_sigalgslen = salglen; + } else { + if (c->conf_sigalgs) { + OPENSSL_free(c->conf_sigalgs); + } + c->conf_sigalgs = sigalgs; + c->conf_sigalgslen = salglen; + } + + return 1; + +err: + OPENSSL_free(sigalgs); + return 0; +} diff --git a/src/ssl/t1_reneg.c b/src/ssl/t1_reneg.c new file mode 100644 index 0000000..2d9fbc0 --- /dev/null +++ b/src/ssl/t1_reneg.c @@ -0,0 +1,247 @@ +/* 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-2009 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 <stdio.h> +#include <assert.h> + +#include <openssl/bytestring.h> +#include <openssl/obj.h> +#include <openssl/err.h> + +#include "ssl_locl.h" + + +/* Add the client's renegotiation binding */ +int ssl_add_clienthello_renegotiate_ext(SSL *s, unsigned char *p, int *len, + int maxlen) { + if (p) { + if (s->s3->previous_client_finished_len + 1 > maxlen) { + OPENSSL_PUT_ERROR(SSL, ssl_add_clienthello_renegotiate_ext, + SSL_R_RENEGOTIATE_EXT_TOO_LONG); + return 0; + } + + /* Length byte */ + *p = s->s3->previous_client_finished_len; + p++; + + memcpy(p, s->s3->previous_client_finished, + s->s3->previous_client_finished_len); + } + + *len = s->s3->previous_client_finished_len + 1; + + return 1; +} + +/* Parse the client's renegotiation binding and abort if it's not right */ +int ssl_parse_clienthello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert) { + CBS renegotiated_connection; + + if (!CBS_get_u8_length_prefixed(cbs, &renegotiated_connection) || + CBS_len(cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_renegotiate_ext, + SSL_R_RENEGOTIATION_ENCODING_ERR); + *out_alert = SSL_AD_DECODE_ERROR; + return 0; + } + + /* Check that the extension matches */ + if (!CBS_mem_equal(&renegotiated_connection, s->s3->previous_client_finished, + s->s3->previous_client_finished_len)) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_clienthello_renegotiate_ext, + SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + + s->s3->send_connection_binding = 1; + + return 1; +} + +/* Add the server's renegotiation binding */ +int ssl_add_serverhello_renegotiate_ext(SSL *s, unsigned char *p, int *len, + int maxlen) { + if (p) { + if (s->s3->previous_client_finished_len + + s->s3->previous_server_finished_len + 1 > + maxlen) { + OPENSSL_PUT_ERROR(SSL, ssl_add_serverhello_renegotiate_ext, + SSL_R_RENEGOTIATE_EXT_TOO_LONG); + return 0; + } + + /* Length byte */ + *p = s->s3->previous_client_finished_len + + s->s3->previous_server_finished_len; + p++; + + memcpy(p, s->s3->previous_client_finished, + s->s3->previous_client_finished_len); + p += s->s3->previous_client_finished_len; + + memcpy(p, s->s3->previous_server_finished, + s->s3->previous_server_finished_len); + } + + *len = s->s3->previous_client_finished_len + + s->s3->previous_server_finished_len + 1; + + return 1; +} + +/* Parse the server's renegotiation binding and abort if it's not right */ +int ssl_parse_serverhello_renegotiate_ext(SSL *s, CBS *cbs, int *out_alert) { + int expected_len = + s->s3->previous_client_finished_len + s->s3->previous_server_finished_len; + CBS renegotiated_connection; + const uint8_t *d; + + /* Check for logic errors */ + assert(!expected_len || s->s3->previous_client_finished_len); + assert(!expected_len || s->s3->previous_server_finished_len); + + /* Parse out the extension contents. */ + if (!CBS_get_u8_length_prefixed(cbs, &renegotiated_connection) || + CBS_len(cbs) != 0) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_renegotiate_ext, + 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_parse_serverhello_renegotiate_ext, + SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + + d = CBS_data(&renegotiated_connection); + if (memcmp(d, s->s3->previous_client_finished, + s->s3->previous_client_finished_len)) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_renegotiate_ext, + SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_HANDSHAKE_FAILURE; + return 0; + } + d += s->s3->previous_client_finished_len; + + if (memcmp(d, s->s3->previous_server_finished, + s->s3->previous_server_finished_len)) { + OPENSSL_PUT_ERROR(SSL, ssl_parse_serverhello_renegotiate_ext, + SSL_R_RENEGOTIATION_MISMATCH); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return 0; + } + s->s3->send_connection_binding = 1; + + return 1; +} diff --git a/src/ssl/test/CMakeLists.txt b/src/ssl/test/CMakeLists.txt new file mode 100644 index 0000000..9992360 --- /dev/null +++ b/src/ssl/test/CMakeLists.txt @@ -0,0 +1,16 @@ +include_directories(../../include) + +add_executable( + bssl_shim + + async_bio.cc + bssl_shim.cc + malloc.cc + packeted_bio.cc + test_config.cc +) + +target_link_libraries(bssl_shim ssl crypto) +if (NOT APPLE AND NOT WIN32) + target_link_libraries(bssl_shim dl) +endif() diff --git a/src/ssl/test/async_bio.cc b/src/ssl/test/async_bio.cc new file mode 100644 index 0000000..c007ffa --- /dev/null +++ b/src/ssl/test/async_bio.cc @@ -0,0 +1,180 @@ +/* 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/mem.h> + + +namespace { + +extern const BIO_METHOD async_bio_method; + +struct async_bio { + bool datagram; + size_t read_quota; + size_t write_quota; +}; + +async_bio *get_data(BIO *bio) { + if (bio->method != &async_bio_method) { + return NULL; + } + return (async_bio *)bio->ptr; +} + +static int async_write(BIO *bio, const char *in, int inl) { + async_bio *a = get_data(bio); + if (a == NULL || bio->next_bio == NULL) { + return 0; + } + + if (a->datagram) { + // Perform writes synchronously; the DTLS implementation drops any packets + // that failed to send. + 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 async_read(BIO *bio, char *out, int outl) { + async_bio *a = get_data(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 async_ctrl(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 async_new(BIO *bio) { + async_bio *a = (async_bio *)OPENSSL_malloc(sizeof(*a)); + if (a == NULL) { + return 0; + } + memset(a, 0, sizeof(*a)); + bio->init = 1; + bio->ptr = (char *)a; + return 1; +} + +static int async_free(BIO *bio) { + if (bio == NULL) { + return 0; + } + + OPENSSL_free(bio->ptr); + bio->ptr = NULL; + bio->init = 0; + bio->flags = 0; + return 1; +} + +static long async_callback_ctrl(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 async_bio_method = { + BIO_TYPE_FILTER, + "async bio", + async_write, + async_read, + NULL /* puts */, + NULL /* gets */, + async_ctrl, + async_new, + async_free, + async_callback_ctrl, +}; + +} // namespace + +BIO *async_bio_create() { + return BIO_new(&async_bio_method); +} + +BIO *async_bio_create_datagram() { + BIO *ret = BIO_new(&async_bio_method); + if (!ret) { + return NULL; + } + get_data(ret)->datagram = true; + return ret; +} + +void async_bio_allow_read(BIO *bio, size_t count) { + async_bio *a = get_data(bio); + if (a == NULL) { + return; + } + a->read_quota += count; +} + +void async_bio_allow_write(BIO *bio, size_t count) { + async_bio *a = get_data(bio); + if (a == NULL) { + return; + } + a->write_quota += count; +} diff --git a/src/ssl/test/async_bio.h b/src/ssl/test/async_bio.h new file mode 100644 index 0000000..2904036 --- /dev/null +++ b/src/ssl/test/async_bio.h @@ -0,0 +1,40 @@ +/* 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> + + +// async_bio_create 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. +BIO *async_bio_create(); + +// async_bio_create_datagram 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. +BIO *async_bio_create_datagram(); + +// async_bio_allow_read increments |bio|'s read quota by |count|. +void async_bio_allow_read(BIO *bio, size_t count); + +// async_bio_allow_write increments |bio|'s write quota by |count|. +void async_bio_allow_write(BIO *bio, size_t count); + + +#endif // HEADER_ASYNC_BIO diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc new file mode 100644 index 0000000..37891b9 --- /dev/null +++ b/src/ssl/test/bssl_shim.cc @@ -0,0 +1,758 @@ +/* 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/base.h> + +#if !defined(OPENSSL_WINDOWS) +#include <arpa/inet.h> +#include <netinet/in.h> +#include <signal.h> +#include <sys/socket.h> +#include <unistd.h> +#endif + +#include <string.h> +#include <sys/types.h> + +#include <openssl/bio.h> +#include <openssl/buf.h> +#include <openssl/bytestring.h> +#include <openssl/ssl.h> + +#include "async_bio.h" +#include "packeted_bio.h" +#include "test_config.h" + +static int usage(const char *program) { + fprintf(stderr, "Usage: %s [flags...]\n", + program); + return 1; +} + +static int g_ex_data_index = 0; + +static bool SetConfigPtr(SSL *ssl, const TestConfig *config) { + return SSL_set_ex_data(ssl, g_ex_data_index, (void *)config) == 1; +} + +static const TestConfig *GetConfigPtr(SSL *ssl) { + return (const TestConfig *)SSL_get_ex_data(ssl, g_ex_data_index); +} + +static EVP_PKEY *LoadPrivateKey(const std::string &file) { + BIO *bio = BIO_new(BIO_s_file()); + if (bio == NULL) { + return NULL; + } + if (!BIO_read_filename(bio, file.c_str())) { + BIO_free(bio); + return NULL; + } + EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + return pkey; +} + +static int early_callback_called = 0; + +static int select_certificate_callback(const struct ssl_early_callback_ctx *ctx) { + early_callback_called = 1; + + const TestConfig *config = GetConfigPtr(ctx->ssl); + + if (config->expected_server_name.empty()) { + return 1; + } + + 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(ctx, 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"); + } + + return 1; +} + +static int skip_verify(int preverify_ok, X509_STORE_CTX *store_ctx) { + return 1; +} + +static int next_protos_advertised_callback(SSL *ssl, + const uint8_t **out, + unsigned int *out_len, + void *arg) { + const TestConfig *config = GetConfigPtr(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 next_proto_select_callback(SSL* ssl, + uint8_t** out, + uint8_t* outlen, + const uint8_t* in, + unsigned inlen, + void* arg) { + const TestConfig *config = GetConfigPtr(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 alpn_select_callback(SSL* ssl, + const uint8_t** out, + uint8_t* outlen, + const uint8_t* in, + unsigned inlen, + void* arg) { + const TestConfig *config = GetConfigPtr(ssl); + if (config->select_alpn.empty()) + return SSL_TLSEXT_ERR_NOACK; + + if (!config->expected_advertised_alpn.empty() && + (config->expected_advertised_alpn.size() != inlen || + 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 int cookie_generate_callback(SSL *ssl, uint8_t *cookie, size_t *cookie_len) { + if (*cookie_len < 32) { + fprintf(stderr, "Insufficient space for cookie\n"); + return 0; + } + *cookie_len = 32; + memset(cookie, 42, *cookie_len); + return 1; +} + +static int cookie_verify_callback(SSL *ssl, const uint8_t *cookie, size_t cookie_len) { + if (cookie_len != 32) { + fprintf(stderr, "Cookie length mismatch.\n"); + return 0; + } + for (size_t i = 0; i < cookie_len; i++) { + if (cookie[i] != 42) { + fprintf(stderr, "Cookie mismatch.\n"); + return 0; + } + } + return 1; +} + +static unsigned psk_client_callback(SSL *ssl, const char *hint, + char *out_identity, + unsigned max_identity_len, + uint8_t *out_psk, unsigned max_psk_len) { + const TestConfig *config = GetConfigPtr(ssl); + + if (strcmp(hint ? 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); + memcpy(out_psk, config->psk.data(), config->psk.size()); + return config->psk.size(); +} + +static unsigned psk_server_callback(SSL *ssl, const char *identity, + uint8_t *out_psk, unsigned max_psk_len) { + const TestConfig *config = GetConfigPtr(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; + } + + memcpy(out_psk, config->psk.data(), config->psk.size()); + return config->psk.size(); +} + +static SSL_CTX *setup_ctx(const TestConfig *config) { + SSL_CTX *ssl_ctx = NULL; + DH *dh = NULL; + + ssl_ctx = SSL_CTX_new(config->is_dtls ? DTLS_method() : TLS_method()); + if (ssl_ctx == NULL) { + goto err; + } + + if (config->is_dtls) { + // DTLS needs read-ahead to function on a datagram BIO. + // + // TODO(davidben): this should not be necessary. DTLS code should only + // expect a datagram BIO. + SSL_CTX_set_read_ahead(ssl_ctx, 1); + } + + if (!SSL_CTX_set_ecdh_auto(ssl_ctx, 1)) { + goto err; + } + + if (!SSL_CTX_set_cipher_list(ssl_ctx, "ALL")) { + goto err; + } + + dh = DH_get_2048_256(NULL); + if (dh == NULL || + !SSL_CTX_set_tmp_dh(ssl_ctx, dh)) { + goto err; + } + + SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH); + + ssl_ctx->select_certificate_cb = select_certificate_callback; + + SSL_CTX_set_next_protos_advertised_cb( + ssl_ctx, next_protos_advertised_callback, NULL); + if (!config->select_next_proto.empty()) { + SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_callback, NULL); + } + + if (!config->select_alpn.empty()) { + SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_callback, NULL); + } + + SSL_CTX_set_cookie_generate_cb(ssl_ctx, cookie_generate_callback); + SSL_CTX_set_cookie_verify_cb(ssl_ctx, cookie_verify_callback); + + ssl_ctx->tlsext_channel_id_enabled_new = 1; + + DH_free(dh); + return ssl_ctx; + + err: + if (dh != NULL) { + DH_free(dh); + } + if (ssl_ctx != NULL) { + SSL_CTX_free(ssl_ctx); + } + return NULL; +} + +static int retry_async(SSL *ssl, int ret, BIO *bio) { + // No error; don't retry. + if (ret >= 0) { + return 0; + } + // 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. + int err = SSL_get_error(ssl, ret); + if (err == SSL_ERROR_WANT_READ) { + async_bio_allow_read(bio, 1); + return 1; + } else if (err == SSL_ERROR_WANT_WRITE) { + async_bio_allow_write(bio, 1); + return 1; + } + return 0; +} + +static int do_exchange(SSL_SESSION **out_session, + SSL_CTX *ssl_ctx, + const TestConfig *config, + bool is_resume, + int fd, + SSL_SESSION *session) { + early_callback_called = 0; + + SSL *ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { + BIO_print_errors_fp(stdout); + return 1; + } + + if (!SetConfigPtr(ssl, config)) { + BIO_print_errors_fp(stdout); + return 1; + } + + if (config->fallback_scsv) { + if (!SSL_enable_fallback_scsv(ssl)) { + BIO_print_errors_fp(stdout); + return 1; + } + } + if (!config->key_file.empty()) { + if (!SSL_use_PrivateKey_file(ssl, config->key_file.c_str(), + SSL_FILETYPE_PEM)) { + BIO_print_errors_fp(stdout); + return 1; + } + } + if (!config->cert_file.empty()) { + if (!SSL_use_certificate_file(ssl, config->cert_file.c_str(), + SSL_FILETYPE_PEM)) { + BIO_print_errors_fp(stdout); + return 1; + } + } + if (config->require_any_client_certificate) { + SSL_set_verify(ssl, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + skip_verify); + } + if (config->false_start) { + SSL_set_mode(ssl, SSL_MODE_HANDSHAKE_CUTTHROUGH); + } + if (config->cbc_record_splitting) { + SSL_set_mode(ssl, SSL_MODE_CBC_RECORD_SPLITTING); + } + if (config->partial_write) { + SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE); + } + if (config->no_tls12) { + SSL_set_options(ssl, SSL_OP_NO_TLSv1_2); + } + if (config->no_tls11) { + SSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + } + if (config->no_tls1) { + SSL_set_options(ssl, SSL_OP_NO_TLSv1); + } + if (config->no_ssl3) { + SSL_set_options(ssl, SSL_OP_NO_SSLv3); + } + if (config->cookie_exchange) { + SSL_set_options(ssl, SSL_OP_COOKIE_EXCHANGE); + } + if (config->tls_d5_bug) { + SSL_set_options(ssl, SSL_OP_TLS_D5_BUG); + } + if (config->allow_unsafe_legacy_renegotiation) { + SSL_set_options(ssl, SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION); + } + if (!config->expected_channel_id.empty()) { + SSL_enable_tls_channel_id(ssl); + } + if (!config->send_channel_id.empty()) { + EVP_PKEY *pkey = LoadPrivateKey(config->send_channel_id); + if (pkey == NULL) { + BIO_print_errors_fp(stdout); + return 1; + } + SSL_enable_tls_channel_id(ssl); + if (!SSL_set1_tls_channel_id(ssl, pkey)) { + EVP_PKEY_free(pkey); + BIO_print_errors_fp(stdout); + return 1; + } + EVP_PKEY_free(pkey); + } + if (!config->host_name.empty()) { + SSL_set_tlsext_host_name(ssl, config->host_name.c_str()); + } + if (!config->advertise_alpn.empty()) { + SSL_set_alpn_protos(ssl, (const uint8_t *)config->advertise_alpn.data(), + config->advertise_alpn.size()); + } + if (!config->psk.empty()) { + SSL_set_psk_client_callback(ssl, psk_client_callback); + SSL_set_psk_server_callback(ssl, psk_server_callback); + } + if (!config->psk_identity.empty() && + !SSL_use_psk_identity_hint(ssl, config->psk_identity.c_str())) { + BIO_print_errors_fp(stdout); + return 1; + } + if (!config->srtp_profiles.empty() && + !SSL_set_srtp_profiles(ssl, config->srtp_profiles.c_str())) { + BIO_print_errors_fp(stdout); + return 1; + } + if (config->enable_ocsp_stapling && + !SSL_enable_ocsp_stapling(ssl)) { + BIO_print_errors_fp(stdout); + return 1; + } + if (config->enable_signed_cert_timestamps && + !SSL_enable_signed_cert_timestamps(ssl)) { + BIO_print_errors_fp(stdout); + return 1; + } + SSL_enable_fastradio_padding(ssl, config->fastradio_padding); + if (config->min_version != 0) { + SSL_set_min_version(ssl, (uint16_t)config->min_version); + } + if (config->max_version != 0) { + SSL_set_max_version(ssl, (uint16_t)config->max_version); + } + if (config->mtu != 0) { + SSL_set_options(ssl, SSL_OP_NO_QUERY_MTU); + SSL_set_mtu(ssl, config->mtu); + } + + BIO *bio = BIO_new_fd(fd, 1 /* take ownership */); + if (bio == NULL) { + BIO_print_errors_fp(stdout); + return 1; + } + if (config->is_dtls) { + BIO *packeted = packeted_bio_create(); + BIO_push(packeted, bio); + bio = packeted; + } + if (config->async) { + BIO *async = + config->is_dtls ? async_bio_create_datagram() : async_bio_create(); + BIO_push(async, bio); + bio = async; + } + SSL_set_bio(ssl, bio, bio); + + if (session != NULL) { + if (SSL_set_session(ssl, session) != 1) { + fprintf(stderr, "failed to set session\n"); + return 2; + } + } + + int ret; + do { + if (config->is_server) { + ret = SSL_accept(ssl); + } else { + ret = SSL_connect(ssl); + } + } while (config->async && retry_async(ssl, ret, bio)); + if (ret != 1) { + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 2; + } + + if (is_resume && (!!SSL_session_reused(ssl) == config->expect_session_miss)) { + fprintf(stderr, "session was%s reused\n", + SSL_session_reused(ssl) ? "" : " not"); + return 2; + } + + if (!config->expected_server_name.empty()) { + const char *server_name = + SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (server_name != config->expected_server_name) { + fprintf(stderr, "servername mismatch (got %s; want %s)\n", + server_name, config->expected_server_name.c_str()); + return 2; + } + + if (!early_callback_called) { + fprintf(stderr, "early callback not called\n"); + return 2; + } + } + + if (!config->expected_certificate_types.empty()) { + uint8_t *certificate_types; + int num_certificate_types = + SSL_get0_certificate_types(ssl, &certificate_types); + if (num_certificate_types != + (int)config->expected_certificate_types.size() || + memcmp(certificate_types, + config->expected_certificate_types.data(), + num_certificate_types) != 0) { + fprintf(stderr, "certificate types mismatch\n"); + return 2; + } + } + + 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() || + memcmp(next_proto, config->expected_next_proto.data(), + next_proto_len) != 0) { + fprintf(stderr, "negotiated next proto mismatch\n"); + return 2; + } + } + + 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() || + memcmp(alpn_proto, config->expected_alpn.data(), + alpn_proto_len) != 0) { + fprintf(stderr, "negotiated alpn proto mismatch\n"); + return 2; + } + } + + 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 2; + } + if (config->expected_channel_id.size() != 64 || + memcmp(config->expected_channel_id.data(), + channel_id, 64) != 0) { + fprintf(stderr, "channel id mismatch\n"); + return 2; + } + } + + if (config->expect_extended_master_secret) { + if (!ssl->session->extended_master_secret) { + fprintf(stderr, "No EMS for session when expected"); + return 2; + } + } + + 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 || + memcmp(config->expected_ocsp_response.data(), data, len) != 0) { + fprintf(stderr, "OCSP response mismatch\n"); + return 2; + } + } + + 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 || + memcmp(config->expected_signed_cert_timestamps.data(), + data, len) != 0) { + fprintf(stderr, "SCT list mismatch\n"); + return 2; + } + } + + if (config->renegotiate) { + if (config->async) { + fprintf(stderr, "--renegotiate is not supported with --async.\n"); + return 2; + } + + SSL_renegotiate(ssl); + + ret = SSL_do_handshake(ssl); + if (ret != 1) { + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 2; + } + + SSL_set_state(ssl, SSL_ST_ACCEPT); + ret = SSL_do_handshake(ssl); + if (ret != 1) { + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 2; + } + } + + if (config->write_different_record_sizes) { + if (config->is_dtls) { + fprintf(stderr, "write_different_record_sizes not supported for DTLS\n"); + return 6; + } + // This mode writes a number of different record sizes in an attempt to + // trip up the CBC record splitting code. + uint8_t buf[32769]; + memset(buf, 0x42, sizeof(buf)); + static const size_t kRecordSizes[] = { + 0, 1, 255, 256, 257, 16383, 16384, 16385, 32767, 32768, 32769}; + for (size_t i = 0; i < sizeof(kRecordSizes) / sizeof(kRecordSizes[0]); + i++) { + int w; + const size_t len = kRecordSizes[i]; + size_t off = 0; + + if (len > sizeof(buf)) { + fprintf(stderr, "Bad kRecordSizes value.\n"); + return 5; + } + + do { + w = SSL_write(ssl, buf + off, len - off); + if (w > 0) { + off += (size_t) w; + } + } while ((config->async && retry_async(ssl, w, bio)) || + (w > 0 && off < len)); + + if (w < 0 || off != len) { + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 4; + } + } + } else { + if (config->shim_writes_first) { + int w; + do { + w = SSL_write(ssl, "hello", 5); + } while (config->async && retry_async(ssl, w, bio)); + } + for (;;) { + uint8_t buf[512]; + int n; + do { + n = SSL_read(ssl, buf, sizeof(buf)); + } while (config->async && retry_async(ssl, n, bio)); + int err = SSL_get_error(ssl, 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 3; + } + /* Accept shutdowns with or without close_notify. + * TODO(davidben): Write tests which distinguish these two cases. */ + break; + } else if (err != SSL_ERROR_NONE) { + if (n > 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return 3; + } + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 3; + } + /* Successfully read data. */ + if (n <= 0) { + fprintf(stderr, "Invalid SSL_get_error output\n"); + return 3; + } + for (int i = 0; i < n; i++) { + buf[i] ^= 0xff; + } + int w; + do { + w = SSL_write(ssl, buf, n); + } while (config->async && retry_async(ssl, w, bio)); + if (w != n) { + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 4; + } + } + } + + if (out_session) { + *out_session = SSL_get1_session(ssl); + } + + SSL_shutdown(ssl); + SSL_free(ssl); + return 0; +} + +int main(int argc, char **argv) { +#if !defined(OPENSSL_WINDOWS) + signal(SIGPIPE, SIG_IGN); +#endif + + if (!SSL_library_init()) { + return 1; + } + g_ex_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + if (g_ex_data_index < 0) { + return 1; + } + + TestConfig config; + if (!ParseConfig(argc - 1, argv + 1, &config)) { + return usage(argv[0]); + } + + SSL_CTX *ssl_ctx = setup_ctx(&config); + if (ssl_ctx == NULL) { + BIO_print_errors_fp(stdout); + return 1; + } + + SSL_SESSION *session = NULL; + int ret = do_exchange(&session, + ssl_ctx, &config, + false /* is_resume */, + 3 /* fd */, NULL /* session */); + if (ret != 0) { + goto out; + } + + if (config.resume) { + ret = do_exchange(NULL, + ssl_ctx, &config, + true /* is_resume */, + 4 /* fd */, + config.is_server ? NULL : session); + if (ret != 0) { + goto out; + } + } + + ret = 0; + +out: + SSL_SESSION_free(session); + SSL_CTX_free(ssl_ctx); + return ret; +} diff --git a/src/ssl/test/malloc.cc b/src/ssl/test/malloc.cc new file mode 100644 index 0000000..6cc0b33 --- /dev/null +++ b/src/ssl/test/malloc.cc @@ -0,0 +1,130 @@ +/* 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/base.h> + +// This file isn't built on ARM or Aarch64 because we link statically in those +// builds and trying to override malloc in a static link doesn't work. +#if defined(__linux__) && !defined(OPENSSL_ARM) && !defined(OPENSSL_AARCH64) + +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <unistd.h> +#include <stdio.h> + +#include <new> + + +/* This file defines overrides for the standard allocation functions that allow + * a given allocation to be made to fail for testing. If the program is run + * with MALLOC_NUMBER_TO_FAIL set to a base-10 number then that allocation will + * return NULL. If MALLOC_ABORT_ON_FAIL is also defined then the allocation + * will abort() rather than return NULL. + * + * This code is not thread safe. */ + +static uint64_t current_malloc_count = 0; +static uint64_t malloc_number_to_fail = 0; +static char failure_enabled = 0, abort_on_fail = 0; +static int in_call = 0; + +extern "C" { +/* These are other names for the standard allocation functions. */ +extern void *__libc_malloc(size_t size); +extern void *__libc_calloc(size_t num_elems, size_t size); +extern void *__libc_realloc(void *ptr, size_t size); +} + +static void exit_handler(void) { + if (failure_enabled && current_malloc_count > malloc_number_to_fail) { + _exit(88); + } +} + +static void cpp_new_handler() { + // Return to try again. It won't fail a second time. + return; +} + +/* should_fail_allocation returns true if the current allocation should fail. */ +static int should_fail_allocation() { + static int init = 0; + char should_fail; + + if (in_call) { + return 0; + } + + in_call = 1; + + if (!init) { + const char *env = getenv("MALLOC_NUMBER_TO_FAIL"); + if (env != NULL && env[0] != 0) { + char *endptr; + malloc_number_to_fail = strtoull(env, &endptr, 10); + if (*endptr == 0) { + failure_enabled = 1; + atexit(exit_handler); + std::set_new_handler(cpp_new_handler); + } + } + abort_on_fail = (NULL != getenv("MALLOC_ABORT_ON_FAIL")); + init = 1; + } + + in_call = 0; + + if (!failure_enabled) { + return 0; + } + + should_fail = (current_malloc_count == malloc_number_to_fail); + current_malloc_count++; + + if (should_fail && abort_on_fail) { + abort(); + } + return should_fail; +} + +extern "C" { + +void *malloc(size_t size) { + if (should_fail_allocation()) { + return NULL; + } + + return __libc_malloc(size); +} + +void *calloc(size_t num_elems, size_t size) { + if (should_fail_allocation()) { + return NULL; + } + + return __libc_calloc(num_elems, size); +} + +void *realloc(void *ptr, size_t size) { + if (should_fail_allocation()) { + return NULL; + } + + return __libc_realloc(ptr, size); +} + +} // extern "C" + +#endif /* defined(linux) && !ARM && !AARCH64 */ diff --git a/src/ssl/test/packeted_bio.cc b/src/ssl/test/packeted_bio.cc new file mode 100644 index 0000000..93b2164 --- /dev/null +++ b/src/ssl/test/packeted_bio.cc @@ -0,0 +1,135 @@ +/* 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 <errno.h> +#include <string.h> + +#include <openssl/mem.h> + + +namespace { + +extern const BIO_METHOD packeted_bio_method; + +static int packeted_write(BIO *bio, const char *in, int inl) { + if (bio->next_bio == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + // Write the length prefix. + uint8_t len_bytes[4]; + len_bytes[0] = (inl >> 24) & 0xff; + len_bytes[1] = (inl >> 16) & 0xff; + len_bytes[2] = (inl >> 8) & 0xff; + len_bytes[3] = inl & 0xff; + int ret = BIO_write(bio->next_bio, len_bytes, sizeof(len_bytes)); + if (ret <= 0) { + BIO_copy_next_retry(bio); + return ret; + } + + // Write the buffer. BIOs for which this operation fails are not supported. + ret = BIO_write(bio->next_bio, in, inl); + assert(ret == inl); + return ret; +} + +static int packeted_read(BIO *bio, char *out, int outl) { + if (bio->next_bio == NULL) { + return 0; + } + + BIO_clear_retry_flags(bio); + + // Read the length prefix. + uint8_t len_bytes[4]; + int ret = BIO_read(bio->next_bio, &len_bytes, sizeof(len_bytes)); + if (ret <= 0) { + BIO_copy_next_retry(bio); + return ret; + } + // BIOs for which a partial length comes back are not supported. + assert(ret == 4); + + uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) | + (len_bytes[2] << 8) | len_bytes[3]; + char *buf = (char *)OPENSSL_malloc(len); + if (buf == NULL) { + return -1; + } + ret = BIO_read(bio->next_bio, buf, len); + assert(ret == (int)len); + + if (outl > (int)len) { + outl = len; + } + memcpy(out, buf, outl); + OPENSSL_free(buf); + return outl; +} + +static long packeted_ctrl(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 packeted_new(BIO *bio) { + bio->init = 1; + return 1; +} + +static int packeted_free(BIO *bio) { + if (bio == NULL) { + return 0; + } + + bio->init = 0; + return 1; +} + +static long packeted_callback_ctrl(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 packeted_bio_method = { + BIO_TYPE_FILTER, + "packeted bio", + packeted_write, + packeted_read, + NULL /* puts */, + NULL /* gets */, + packeted_ctrl, + packeted_new, + packeted_free, + packeted_callback_ctrl, +}; + +} // namespace + +BIO *packeted_bio_create() { + return BIO_new(&packeted_bio_method); +} diff --git a/src/ssl/test/packeted_bio.h b/src/ssl/test/packeted_bio.h new file mode 100644 index 0000000..384bd64 --- /dev/null +++ b/src/ssl/test/packeted_bio.h @@ -0,0 +1,32 @@ +/* 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/bio.h> + + +// packeted_bio_create creates a filter BIO for testing protocols which expect +// datagram BIOs. It implements a reliable datagram socket and reads and writes +// packets by prefixing each packet with a big-endian 32-bit length. It must be +// layered over a reliable blocking stream BIO. +// +// Note: packeted_bio_create exists because a SOCK_DGRAM socketpair on OS X is +// does not block the caller, unlike on Linux. Writes simply fail with +// ENOBUFS. POSIX also does not guarantee that such sockets are reliable. +BIO *packeted_bio_create(); + + +#endif // HEADER_PACKETED_BIO diff --git a/src/ssl/test/runner/alert.go b/src/ssl/test/runner/alert.go new file mode 100644 index 0000000..b48ab2a --- /dev/null +++ b/src/ssl/test/runner/alert.go @@ -0,0 +1,77 @@ +// 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 main + +import "strconv" + +type alert uint8 + +const ( + // alert level + alertLevelWarning = 1 + alertLevelError = 2 +) + +const ( + alertCloseNotify alert = 0 + alertUnexpectedMessage alert = 10 + alertBadRecordMAC alert = 20 + alertDecryptionFailed alert = 21 + alertRecordOverflow alert = 22 + alertDecompressionFailure alert = 30 + alertHandshakeFailure alert = 40 + 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 + alertUserCanceled alert = 90 + alertNoRenegotiation alert = 100 +) + +var alertText = map[alert]string{ + alertCloseNotify: "close notify", + alertUnexpectedMessage: "unexpected message", + alertBadRecordMAC: "bad record MAC", + alertDecryptionFailed: "decryption failed", + alertRecordOverflow: "record overflow", + alertDecompressionFailure: "decompression failure", + alertHandshakeFailure: "handshake failure", + 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", + alertUserCanceled: "user canceled", + alertNoRenegotiation: "no renegotiation", +} + +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/src/ssl/test/runner/cert.pem b/src/ssl/test/runner/cert.pem new file mode 100644 index 0000000..4de4f49 --- /dev/null +++ b/src/ssl/test/runner/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/src/ssl/test/runner/channel_id_key.pem b/src/ssl/test/runner/channel_id_key.pem new file mode 100644 index 0000000..604752b --- /dev/null +++ b/src/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/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go new file mode 100644 index 0000000..89e75c8 --- /dev/null +++ b/src/ssl/test/runner/cipher_suites.go @@ -0,0 +1,395 @@ +// 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 main + +import ( + "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) +} + +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. + suiteTLS12 + // 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 +) + +// 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 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(key, fixedNonce []byte) cipher.AEAD +} + +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_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE | suiteNoDTLS, cipherRC4, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteNoDTLS, cipherRC4, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, cipherAES, macSHA384, nil}, + {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, + {TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, dheRSAKA, suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, dheRSAKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, 32, 32, 16, dheRSAKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_DHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, dheRSAKA, 0, cipherAES, macSHA1, nil}, + {TLS_DHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, dheRSAKA, 0, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM}, + {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, suiteNoDTLS, cipherRC4, macSHA1, nil}, + {TLS_RSA_WITH_RC4_128_MD5, 16, 16, 0, rsaKA, suiteNoDTLS, cipherRC4, macMD5, nil}, + {TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_RSA_WITH_AES_256_CBC_SHA256, 32, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil}, + {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil}, + {TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, dheRSAKA, 0, cipher3DES, macSHA1, nil}, + {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil}, + {TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdhePSKKA, suiteECDHE | suiteTLS12 | suitePSK, nil, nil, aeadAESGCM}, + {TLS_PSK_WITH_RC4_128_SHA, 16, 20, 0, pskKA, suiteNoDTLS | suitePSK, cipherRC4, macSHA1, nil}, + {TLS_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil}, + {TLS_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil}, +} + +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(key, fixedNonce []byte) cipher.AEAD { + 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) + + return &fixedNonceAEAD{nonce1, nonce2, aead} +} + +// 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{} +} + +func ecdheECDSAKA(version uint16) keyAgreement { + return &ecdheKeyAgreement{ + auth: &signedKeyAgreement{ + sigType: signatureECDSA, + version: version, + }, + } +} + +func ecdheRSAKA(version uint16) keyAgreement { + return &ecdheKeyAgreement{ + auth: &signedKeyAgreement{ + sigType: signatureRSA, + version: version, + }, + } +} + +func dheRSAKA(version uint16) keyAgreement { + return &dheKeyAgreement{ + auth: &signedKeyAgreement{ + sigType: signatureRSA, + 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 { + for _, suite := range cipherSuites { + if suite.id == want { + return suite + } + } + return nil + } + } + 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_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 + fallbackSCSV uint16 = 0x5600 +) + +// Additional cipher suite IDs, not IANA-assigned. +const ( + TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0xcafe +) diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go new file mode 100644 index 0000000..7aaf9a2 --- /dev/null +++ b/src/ssl/test/runner/common.go @@ -0,0 +1,953 @@ +// 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 main + +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 +) + +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 = VersionTLS12 +) + +// 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 + typeCertificate uint8 = 11 + typeServerKeyExchange uint8 = 12 + typeCertificateRequest uint8 = 13 + typeServerHelloDone uint8 = 14 + typeCertificateVerify uint8 = 15 + typeClientKeyExchange uint8 = 16 + typeFinished uint8 = 20 + typeCertificateStatus uint8 = 22 + typeNextProtocol uint8 = 67 // Not IANA assigned + typeEncryptedExtensions 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 + 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 ( + CurveP256 CurveID = 23 + CurveP384 CurveID = 24 + CurveP521 CurveID = 25 +) + +// TLS Elliptic Curve Point Formats +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-9 +const ( + pointFormatUncompressed uint8 = 0 +) + +// 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 +) + +// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1) +const ( + hashMD5 uint8 = 1 + hashSHA1 uint8 = 2 + hashSHA224 uint8 = 3 + hashSHA256 uint8 = 4 + hashSHA384 uint8 = 5 + hashSHA512 uint8 = 6 +) + +// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1) +const ( + signatureRSA uint8 = 1 + signatureECDSA uint8 = 3 +) + +// signatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct. See +// RFC 5246, section A.4.1. +type signatureAndHash struct { + signature, hash uint8 +} + +// supportedSKXSignatureAlgorithms contains the signature and hash algorithms +// that the code advertises as supported in a TLS 1.2 ClientHello. +var supportedSKXSignatureAlgorithms = []signatureAndHash{ + {signatureRSA, hashSHA256}, + {signatureECDSA, hashSHA256}, + {signatureRSA, hashSHA1}, + {signatureECDSA, hashSHA1}, +} + +// supportedClientCertSignatureAlgorithms contains the signature and hash +// algorithms that the code advertises as supported in a TLS 1.2 +// CertificateRequest. +var supportedClientCertSignatureAlgorithms = []signatureAndHash{ + {signatureRSA, hashSHA256}, + {signatureECDSA, hashSHA256}, +} + +// 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 +) + +// 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 +} + +// 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 +} + +// 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 + + // 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 + + // SRTPProtectionProfiles, if not nil, is the list of SRTP + // protection profiles to offer in DTLS-SRTP. + SRTPProtectionProfiles []uint16 + + // SignatureAndHashes, if not nil, overrides the default set of + // supported signature and hash algorithms to advertise in + // CertificateRequest. + SignatureAndHashes []signatureAndHash + + // 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 ProtocolBugs struct { + // InvalidSKXSignature specifies that the signature in a + // ServerKeyExchange message should be invalid. + InvalidSKXSignature bool + + // InvalidSKXCurve causes the curve ID in the ServerKeyExchange message + // to be wrong. + InvalidSKXCurve 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 + + // SkipServerKeyExchange causes the server to skip sending + // ServerKeyExchange messages. + SkipServerKeyExchange bool + + // SkipChangeCipherSpec causes the implementation to skip + // sending the ChangeCipherSpec message (and adjusting cipher + // state accordingly for the Finished message). + SkipChangeCipherSpec 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 + + // FragmentAcrossChangeCipherSpec causes the implementation to fragment + // the Finished (or NextProto) message around the ChangeCipherSpec + // messages. + FragmentAcrossChangeCipherSpec bool + + // SkipNewSessionTicket causes the server to skip sending the + // NewSessionTicket message despite promising to in ServerHello. + SkipNewSessionTicket 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 + + // 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. + 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 + + // SendSpuriousAlert will cause an spurious, unwanted alert to be sent. + SendSpuriousAlert bool + + // RsaClientKeyExchangeVersion, if non-zero, causes the client to send a + // ClientKeyExchange with the specified version rather than the + // client_version when performing the RSA key exchange. + RsaClientKeyExchangeVersion uint16 + + // 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 a different + // TLS version in the ClientHello than the maximum supported version. + SendClientVersion uint16 + + // SkipHelloVerifyRequest causes a DTLS server to skip the + // HelloVerifyRequest message. + SkipHelloVerifyRequest bool + + // 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 + + // SSL3RSAKeyExchange causes the client to always send an RSA + // ClientKeyExchange message without the two-byte length + // prefix, as if it were SSL3. + SSL3RSAKeyExchange bool + + // SkipCipherVersionCheck causes the server to negotiate + // TLS 1.2 ciphers in earlier versions of TLS. + SkipCipherVersionCheck bool + + // 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 on the server. This is to test that server preference + // of ALPN works regardless of their relative order. + SwapNPNAndALPN bool + + // AllowSessionVersionMismatch causes the server to resume sessions + // regardless of the version associated with the session. + AllowSessionVersionMismatch bool + + // CorruptTicket causes a client to corrupt a session ticket before + // sending it in a resume handshake. + CorruptTicket bool + + // OversizedSessionId causes the session id that is sent with a ticket + // resumption attempt to be too large (33 bytes). + OversizedSessionId 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. + NoExtendedMasterSecret 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 causes the client to behave as if it + // didn't support the renegotiation info extension. + NoRenegotiationInfo bool + + // SequenceNumberIncrement, if non-zero, causes outgoing sequence + // numbers in DTLS to increment by that value rather by 1. This is to + // stress the replay bitmap window by simulating extreme packet loss and + // retransmit at the record layer. + SequenceNumberIncrement uint64 + + // RSAServerKeyExchange, if true, causes the server to send a + // ServerKeyExchange message in the plain RSA key exchange. + RSAServerKeyExchange 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 + + // NoSignatureAndHashes, 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. + NoSignatureAndHashes bool + + // RequireSameRenegoClientVersion, if true, causes the server + // to require that all ClientHellos match in offered version + // across a renego. + RequireSameRenegoClientVersion bool + + // RequireFastradioPadding, if true, requires that ClientHello messages + // be at least 1000 bytes long. + RequireFastradioPadding bool + + // ExpectInitialRecordVersion, if non-zero, is the expected + // version of the records before the version is determined. + ExpectInitialRecordVersion 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 + + // AppDataAfterChangeCipherSpec, if not null, causes application data to + // be sent immediately after ChangeCipherSpec. + AppDataAfterChangeCipherSpec []byte +} + +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() uint16 { + if c == nil || c.MinVersion == 0 { + return minVersion + } + return c.MinVersion +} + +func (c *Config) maxVersion() uint16 { + if c == nil || c.MaxVersion == 0 { + return maxVersion + } + return c.MaxVersion +} + +var defaultCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521} + +func (c *Config) curvePreferences() []CurveID { + if c == nil || len(c.CurvePreferences) == 0 { + return defaultCurvePreferences + } + return c.CurvePreferences +} + +// mutualVersion returns the protocol version to use given the advertised +// version of the peer. +func (c *Config) mutualVersion(vers uint16) (uint16, bool) { + minVersion := c.minVersion() + maxVersion := c.maxVersion() + + if vers < minVersion { + return 0, false + } + if vers > maxVersion { + vers = maxVersion + } + return vers, true +} + +// 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) signatureAndHashesForServer() []signatureAndHash { + if c != nil && c.SignatureAndHashes != nil { + return c.SignatureAndHashes + } + return supportedClientCertSignatureAlgorithms +} + +func (c *Config) signatureAndHashesForClient() []signatureAndHash { + if c != nil && c.SignatureAndHashes != nil { + return c.SignatureAndHashes + } + return supportedSKXSignatureAlgorithms +} + +// 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 isSupportedSignatureAndHash(sigHash signatureAndHash, sigHashes []signatureAndHash) bool { + for _, s := range sigHashes { + if s == sigHash { + return true + } + } + return false +} diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go new file mode 100644 index 0000000..d4a6817 --- /dev/null +++ b/src/ssl/test/runner/conn.go @@ -0,0 +1,1229 @@ +// 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 main + +import ( + "bytes" + "crypto/cipher" + "crypto/ecdsa" + "crypto/subtle" + "crypto/x509" + "errors" + "fmt" + "io" + "net" + "sync" + "time" +) + +// 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 + didResume bool // whether this connection was a session resumption + extendedMasterSecret bool // whether this session used an extended master secret + cipherSuite uint16 + ocspResponse []byte // stapled OCSP response + 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 + + 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 + + // DTLS state + sendHandshakeSeq uint16 + recvHandshakeSeq uint16 + handMsg []byte // pending assembled handshake message + handMsgLen int // handshake message length, not including the header + + 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 +} + +// 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 + bfree *block // list of free blocks + + nextCipher interface{} // next encryption state + nextMac macFunction // next MAC algorithm + + // used to save allocating a new buffer for each MAC. + inDigestBuf, outDigestBuf []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() + return nil +} + +// 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 + + if isOutgoing && hc.config.Bugs.SequenceNumberIncrement != 0 { + increment = hc.config.Bugs.SequenceNumberIncrement + } + } + 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") + } +} + +// incEpoch resets the sequence number. In DTLS, it increments the +// epoch half of the sequence number. +func (hc *halfConn) incEpoch() { + limit := 0 + 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") + } + } + limit = 2 + } + seq := hc.seq[limit:] + for i := range seq { + seq[i] = 0 + } +} + +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, and an optional alert value. +func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, 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 cipher.AEAD: + explicitIVLen = 8 + if len(payload) < explicitIVLen { + return false, 0, alertBadRecordMAC + } + nonce := payload[:8] + payload = payload[8:] + + var additionalData [13]byte + 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, 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, 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. + default: + panic("unknown cipher type") + } + } + + // check, strip mac + if hc.mac != nil { + if len(payload) < macSize { + return false, 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, alertBadRecordMAC + } + hc.inDigestBuf = localMAC + } + hc.incSeq(false) + + return true, recordHeaderLen + explicitIVLen, 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) (bool, alert) { + recordHeaderLen := hc.recordHeaderLen() + + // mac + if hc.mac != nil { + mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[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 { + switch c := hc.cipher.(type) { + case cipher.Stream: + c.XORKeyStream(payload, payload) + case cipher.AEAD: + payloadLen := len(b.data) - recordHeaderLen - explicitIVLen + b.resize(len(b.data) + c.Overhead()) + nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + payload := b.data[recordHeaderLen+explicitIVLen:] + payload = payload[:payloadLen] + + var additionalData [13]byte + copy(additionalData[:], hc.seq[:]) + 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) + 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) { + if c.isDTLS { + return c.dtlsDoReadRecord(want) + } + + recordHeaderLen := tlsRecordHeaderLen + + 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. + // if err == io.EOF { + // 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]) + if c.haveVers { + if vers != c.vers { + c.sendAlert(alertProtocolVersion) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, c.vers)) + } + } else { + if expect := c.config.Bugs.ExpectInitialRecordVersion; 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, err := c.in.decrypt(b) + if !ok { + c.in.setErrorLocked(c.sendAlert(err)) + } + b.off = off + 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 { + c.sendAlert(alertInternalError) + return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete")) + } + } + +Again: + typ, b, err := c.doReadRecord(want) + if err != nil { + return err + } + data := b.data[b.off:] + if len(data) > maxPlaintext { + 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: + // 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: + // TODO(rsc): Should at least pick off connection close. + if typ != want { + // A client might need to process a HelloRequest from + // the server, thus receiving a handshake message when + // application data is expected is ok. Moreover, a DTLS + // peer who sends Finished second may retransmit the + // final leg. BoringSSL retrainsmits on an internal + // timer, so this may also occur in test code. + if !c.isClient && !c.isDTLS { + 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(err alert) error { + switch err { + case alertNoRenegotiation, alertCloseNotify: + c.tmp[0] = alertLevelWarning + default: + c.tmp[0] = alertLevelError + } + 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 { + c.writeRecord(recordTypeAlert, c.tmp[0:2]) + } + // closeNotify is a special case in that it isn't an error: + if err != alertCloseNotify { + 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 { + c.out.Lock() + defer c.out.Unlock() + return c.sendAlertLocked(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 c.isDTLS { + return c.dtlsWriteRecord(typ, data) + } + + recordHeaderLen := tlsRecordHeaderLen + b := c.out.newBlock() + first := true + isClientHello := typ == recordTypeHandshake && len(data) > 0 && data[0] == typeClientHello + for len(data) > 0 { + m := len(data) + if m > maxPlaintext { + 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 _, ok := c.out.cipher.(cipher.AEAD); ok { + 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) + 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 + } + 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) + _, 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) 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 != 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 typeNewSessionTicket: + m = new(newSessionTicketMsg) + case typeCertificate: + m = new(certificateMsg) + case typeCertificateRequest: + m = &certificateRequestMsg{ + hasSignatureAndHash: c.vers >= VersionTLS12, + } + case typeCertificateStatus: + m = new(certificateStatusMsg) + case typeServerKeyExchange: + m = new(serverKeyExchangeMsg) + case typeServerHelloDone: + m = new(serverHelloDoneMsg) + case typeClientKeyExchange: + m = new(clientKeyExchangeMsg) + case typeCertificateVerify: + m = &certificateVerifyMsg{ + hasSignatureAndHash: c.vers >= VersionTLS12, + } + case typeNextProtocol: + m = new(nextProtoMsg) + case typeFinished: + m = new(finishedMsg) + case typeHelloVerifyRequest: + m = new(helloVerifyRequestMsg) + case typeEncryptedExtensions: + m = new(encryptedExtensionsMsg) + 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 +} + +// 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() + + if err := c.out.err; err != nil { + return 0, err + } + + if !c.handshakeComplete { + return 0, alertInternalError + } + + if c.config.Bugs.SendSpuriousAlert { + c.sendAlertLocked(alertRecordOverflow) + } + + // 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) handleRenegotiation() error { + c.handshakeComplete = false + if !c.isClient { + panic("renegotiation should only happen for a client") + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + _, ok := msg.(*helloRequestMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return alertUnexpectedMessage + } + + return c.Handshake() +} + +func (c *Conn) Renegotiate() error { + if !c.isClient { + helloReq := new(helloRequestMsg) + c.writeRecord(recordTypeHandshake, helloReq.marshal()) + } + + 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 && !c.isDTLS { + // We received handshake bytes, indicating the + // start of a renegotiation or a DTLS retransmit. + if err := c.handleRenegotiation(); 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 { + alertErr = c.sendAlert(alertCloseNotify) + } + + 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.isClient { + c.handshakeErr = c.clientHandshake() + } else { + c.handshakeErr = c.serverHandshake() + } + 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 + state.PeerCertificates = c.peerCertificates + state.VerifiedChains = c.verifiedChains + state.ServerName = c.serverName + state.ChannelID = c.channelID + state.SRTPProtectionProfile = c.srtpProtectionProfile + } + + 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) +} diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go new file mode 100644 index 0000000..a395980 --- /dev/null +++ b/src/ssl/test/runner/dtls.go @@ -0,0 +1,342 @@ +// 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 main + +import ( + "bytes" + "crypto/cipher" + "errors" + "fmt" + "io" + "net" +) + +func versionToWire(vers uint16, isDTLS bool) uint16 { + if isDTLS { + return ^(vers - 0x0201) + } + return vers +} + +func wireToVersion(vers uint16, isDTLS bool) uint16 { + if isDTLS { + return ^vers + 0x0201 + } + return vers +} + +func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) { +Again: + 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. + 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) + } + + // 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 := wireToVersion(uint16(b.data[1])<<8|uint16(b.data[2]), c.isDTLS) + if c.haveVers { + if vers != c.vers { + c.sendAlert(alertProtocolVersion) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, c.vers)) + } + } else { + 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)) + } + } + seq := b.data[3:11] + if !bytes.Equal(seq[:2], c.in.seq[:2]) { + // If the epoch didn't match, silently drop the record. + // BoringSSL retransmits on an internal timer, so it may flakily + // revisit the previous epoch if retransmiting ChangeCipherSpec + // and Finished. + goto Again + } + // For test purposes, we assume a reliable channel. Require + // that the explicit sequence number matches the incrementing + // one we maintain. A real implementation would maintain a + // replay window and such. + if !bytes.Equal(seq, c.in.seq[:]) { + c.sendAlert(alertIllegalParameter) + return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: bad sequence number")) + } + 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, err := c.in.decrypt(b) + if !ok { + c.in.setErrorLocked(c.sendAlert(err)) + } + b.off = off + return typ, b, nil +} + +func (c *Conn) dtlsWriteRecord(typ recordType, data []byte) (n int, err error) { + recordHeaderLen := dtlsRecordHeaderLen + maxLen := c.config.Bugs.MaxHandshakeRecordLength + if maxLen <= 0 { + maxLen = 1024 + } + + b := c.out.newBlock() + + var header []byte + if typ == recordTypeHandshake { + // Handshake messages have to be modified to include + // fragment offset and length and with the header + // replicated. Save the 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.) + if len(data) < 4 { + // This should not happen. + panic(data) + } + header = data[:4] + data = data[4:] + } + + firstRun := true + for firstRun || len(data) > 0 { + firstRun = false + m := len(data) + var fragment []byte + // Handshake messages get fragmented. Other records we + // pass-through as is. DTLS should be a packet + // interface. + if typ == recordTypeHandshake { + if m > maxLen { + m = maxLen + } + + // Standard handshake header. + fragment = make([]byte, 0, 12+m) + fragment = append(fragment, header...) + // message_seq + fragment = append(fragment, byte(c.sendHandshakeSeq>>8), byte(c.sendHandshakeSeq)) + // fragment_offset + fragment = append(fragment, byte(n>>16), byte(n>>8), byte(n)) + // fragment_length + fragment = append(fragment, byte(m>>16), byte(m>>8), byte(m)) + fragment = append(fragment, data[:m]...) + } else { + fragment = data[:m] + } + + // Send the fragment. + explicitIVLen := 0 + explicitIVIsSeq := false + + if cbc, ok := c.out.cipher.(cbcMode); ok { + // Block cipher modes have an explicit IV. + explicitIVLen = cbc.BlockSize() + } else if _, ok := c.out.cipher.(cipher.AEAD); ok { + 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 c.out.cipher != nil { + panic("Unknown cipher") + } + b.resize(recordHeaderLen + explicitIVLen + len(fragment)) + 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.seq[0:]) + b.data[11] = byte(len(fragment) >> 8) + b.data[12] = byte(len(fragment)) + 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:], fragment) + c.out.encrypt(b, explicitIVLen) + + // TODO(davidben): A real DTLS implementation needs to + // retransmit handshake messages. For testing + // purposes, we don't actually care. + _, err = c.conn.Write(b.data) + if err != nil { + break + } + n += m + data = data[m:] + } + c.out.freeBlock(b) + + // Increment the handshake sequence number for the next + // handshake message. + if typ == recordTypeHandshake { + c.sendHandshakeSeq++ + } + + 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) dtlsDoReadHandshake() ([]byte, error) { + // Assemble a full handshake message. For test purposes, this + // implementation assumes fragments arrive in order, but tolerates + // retransmits. 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) + + if fragSeq < c.recvHandshakeSeq { + // BoringSSL retransmits based on an internal timer, so + // it may flakily retransmit part of a handshake + // message. Ignore those fragments. + // + // TODO(davidben): Revise this if BoringSSL's retransmit + // logic is made more deterministic. + continue + } else if fragSeq > c.recvHandshakeSeq { + return nil, errors.New("dtls: handshake messages sent out of order") + } + + // 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/src/ssl/test/runner/ecdsa_cert.pem b/src/ssl/test/runner/ecdsa_cert.pem new file mode 100644 index 0000000..50bcbf5 --- /dev/null +++ b/src/ssl/test/runner/ecdsa_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/src/ssl/test/runner/ecdsa_key.pem b/src/ssl/test/runner/ecdsa_key.pem new file mode 100644 index 0000000..b9116f0 --- /dev/null +++ b/src/ssl/test/runner/ecdsa_key.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIAcPCHJ61KBKnN1ZyU2JaHcItW/JXTB3DujRyc4Ki7RqoAoGCCqGSM49 +AwEHoUQDQgAE5itp4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY ++cPEox5W4nyDSNsWGhz1HX7xlC1Lz3IiwQ== +-----END EC PRIVATE KEY----- diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go new file mode 100644 index 0000000..f297fc1 --- /dev/null +++ b/src/ssl/test/runner/handshake_client.go @@ -0,0 +1,910 @@ +// 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 main + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/subtle" + "crypto/x509" + "encoding/asn1" + "errors" + "fmt" + "io" + "math/big" + "net" + "strconv" +) + +type clientHandshakeState struct { + c *Conn + serverHello *serverHelloMsg + hello *clientHelloMsg + suite *cipherSuite + finishedHash finishedHash + masterSecret []byte + session *ClientSessionState +} + +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 == 0 || l > 255 { + return errors.New("tls: invalid NextProtos value") + } else { + nextProtosLength += 1 + l + } + } + if nextProtosLength > 0xffff { + return errors.New("tls: NextProtos values too large") + } + + hello := &clientHelloMsg{ + isDTLS: c.isDTLS, + vers: c.config.maxVersion(), + compressionMethods: []uint8{compressionNone}, + random: make([]byte, 32), + ocspStapling: true, + serverName: c.config.ServerName, + supportedCurves: c.config.curvePreferences(), + 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, + npnLast: c.config.Bugs.SwapNPNAndALPN, + extendedMasterSecret: c.config.maxVersion() >= VersionTLS10, + srtpProtectionProfiles: c.config.SRTPProtectionProfiles, + srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer, + } + + if c.config.Bugs.SendClientVersion != 0 { + hello.vers = c.config.Bugs.SendClientVersion + } + + if c.config.Bugs.NoExtendedMasterSecret { + hello.extendedMasterSecret = false + } + + 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.config.Bugs.NoRenegotiationInfo { + hello.secureRenegotiation = nil + } + + 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 hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 { + continue + } + // Don't advertise non-DTLS cipher suites on DTLS. + if c.isDTLS && suite.flags&suiteNoDTLS != 0 { + continue + } + hello.cipherSuites = append(hello.cipherSuites, suiteId) + continue NextCipherSuite + } + } + + 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 hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes { + hello.signatureAndHashes = c.config.signatureAndHashesForClient() + } + + 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) + 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 + for _, id := range hello.cipherSuites { + if id == candidateSession.cipherSuite { + cipherSuiteOk = true + break + } + } + + versOk := candidateSession.vers >= c.config.minVersion() && + candidateSession.vers <= c.config.maxVersion() + if ticketOk && versOk && cipherSuiteOk { + session = candidateSession + } + } + } + + if session != nil { + if session.sessionTicket != nil { + hello.sessionTicket = session.sessionTicket + if c.config.Bugs.CorruptTicket { + hello.sessionTicket = make([]byte, len(session.sessionTicket)) + copy(hello.sessionTicket, session.sessionTicket) + if len(hello.sessionTicket) > 0 { + offset := 40 + if offset > len(hello.sessionTicket) { + offset = len(hello.sessionTicket) - 1 + } + hello.sessionTicket[offset] ^= 0x40 + } + } + // 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.OversizedSessionId { + sessionIdLen = 33 + } + 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 + } + } + + 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 { + helloBytes = hello.marshal() + c.writeRecord(recordTypeHandshake, helloBytes) + } + + msg, err := c.readHandshake() + if err != nil { + return err + } + + if c.isDTLS { + helloVerifyRequest, ok := msg.(*helloVerifyRequestMsg) + if ok { + if helloVerifyRequest.vers != VersionTLS10 { + // 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) + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + } + + serverHello, ok := msg.(*serverHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(serverHello, msg) + } + + c.vers, ok = c.config.mutualVersion(serverHello.vers) + if !ok { + c.sendAlert(alertProtocolVersion) + return fmt.Errorf("tls: server selected unsupported protocol version %x", serverHello.vers) + } + c.haveVers = true + + suite := mutualCipherSuite(c.config.cipherSuites(), serverHello.cipherSuite) + if suite == nil { + c.sendAlert(alertHandshakeFailure) + return fmt.Errorf("tls: server selected an unsupported cipher suite") + } + + if len(c.clientVerify) > 0 && !c.config.Bugs.NoRenegotiationInfo { + var expectedRenegInfo []byte + expectedRenegInfo = append(expectedRenegInfo, c.clientVerify...) + expectedRenegInfo = append(expectedRenegInfo, c.serverVerify...) + if !bytes.Equal(serverHello.secureRenegotiation, expectedRenegInfo) { + c.sendAlert(alertHandshakeFailure) + return fmt.Errorf("tls: renegotiation mismatch") + } + } + + hs := &clientHandshakeState{ + c: c, + serverHello: serverHello, + hello: hello, + suite: suite, + finishedHash: newFinishedHash(c.vers, suite), + session: session, + } + + hs.writeHash(helloBytes, hs.c.sendHandshakeSeq-1) + hs.writeServerHash(hs.serverHello.marshal()) + + if c.config.Bugs.EarlyChangeCipherSpec > 0 { + hs.establishKeys() + c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + } + + 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(); err != nil { + return err + } + if err := hs.sendFinished(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(isResume); err != nil { + return err + } + if err := hs.readSessionTicket(); err != nil { + return err + } + if err := hs.readFinished(); err != nil { + return err + } + } + + if sessionCache != nil && hs.session != nil && session != hs.session { + sessionCache.Put(cacheKey, hs.session) + } + + c.didResume = isResume + c.handshakeComplete = true + c.cipherSuite = suite.id + 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 || len(certMsg.certificates) == 0 { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(certMsg, msg) + } + hs.writeServerHash(certMsg.marshal()) + + certs := make([]*x509.Certificate, len(certMsg.certificates)) + for i, asn1Data := range certMsg.certificates { + cert, err := x509.ParseCertificate(asn1Data) + if err != nil { + c.sendAlert(alertBadCertificate) + return errors.New("tls: failed to parse certificate from server: " + err.Error()) + } + certs[i] = cert + } + leaf = certs[0] + + 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) + } + c.verifiedChains, err = leaf.Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return err + } + } + + switch leaf.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", leaf.PublicKey) + } + + c.peerCertificates = certs + } + + if hs.serverHello.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 + } + + msg, err = c.readHandshake() + if err != nil { + return err + } + } + + var chainToSend *Certificate + var certRequested bool + certReq, ok := msg.(*certificateRequestMsg) + if ok { + certRequested = true + + // 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. + + hs.writeServerHash(certReq.marshal()) + + var rsaAvail, ecdsaAvail bool + 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 !rsaAvail && !ecdsaAvail { + 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 { + if x509Cert, err = x509.ParseCertificate(cert); err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + } + } + + switch { + case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: + case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: + default: + continue findCert + } + + if len(certReq.certificateAuthorities) == 0 { + // they gave us an empty list, so just take the + // first RSA cert from c.config.Certificates + chainToSend = &chain + break findCert + } + + for _, ca := range certReq.certificateAuthorities { + if bytes.Equal(x509Cert.RawIssuer, ca) { + chainToSend = &chain + break findCert + } + } + } + } + + 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, even if it's empty because we don't have a + // certificate to send. + if certRequested { + certMsg := new(certificateMsg) + if chainToSend != nil { + certMsg.certificates = chainToSend.Certificate + } + 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.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 { + var signed []byte + certVerify := &certificateVerifyMsg{ + hasSignatureAndHash: c.vers >= VersionTLS12, + } + + switch key := c.config.Certificates[0].PrivateKey.(type) { + case *ecdsa.PrivateKey: + certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, signatureECDSA) + if err != nil { + break + } + var digest []byte + digest, _, err = hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret) + if err != nil { + break + } + var r, s *big.Int + r, s, err = ecdsa.Sign(c.config.rand(), key, digest) + if err == nil { + signed, err = asn1.Marshal(ecdsaSignature{r, s}) + } + case *rsa.PrivateKey: + certVerify.signatureAndHash, err = hs.finishedHash.selectClientCertSignatureAlgorithm(certReq.signatureAndHashes, signatureRSA) + if err != nil { + break + } + var digest []byte + var hashFunc crypto.Hash + digest, hashFunc, err = hs.finishedHash.hashForClientCertificate(certVerify.signatureAndHash, hs.masterSecret) + if err != nil { + break + } + signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest) + default: + err = errors.New("unknown private key type") + } + if err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to sign handshake with client certificate: " + err.Error()) + } + certVerify.signature = signed + + hs.writeClientHash(certVerify.marshal()) + c.writeRecord(recordTypeHandshake, certVerify.marshal()) + } + + hs.finishedHash.discardHandshakeBuffer() + + 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) + 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(clientKey, clientIV) + serverCipher = hs.suite.aead(serverKey, serverIV) + } + + c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) + c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) + 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. + 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.serverHello.compressionMethod != compressionNone { + c.sendAlert(alertUnexpectedMessage) + return false, errors.New("tls: server selected unsupported compression format") + } + + clientDidNPN := hs.hello.nextProtoNeg + clientDidALPN := len(hs.hello.alpnProtocols) > 0 + serverHasNPN := hs.serverHello.nextProtoNeg + serverHasALPN := len(hs.serverHello.alpnProtocol) > 0 + + if !clientDidNPN && serverHasNPN { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("server advertised unrequested NPN extension") + } + + if !clientDidALPN && serverHasALPN { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("server advertised unrequested ALPN extension") + } + + if serverHasNPN && serverHasALPN { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("server advertised both NPN and ALPN extensions") + } + + if serverHasALPN { + c.clientProtocol = hs.serverHello.alpnProtocol + c.clientProtocolFallback = false + c.usedALPN = true + } + + if !hs.hello.channelIDSupported && hs.serverHello.channelIDRequested { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("server advertised unrequested Channel ID extension") + } + + if hs.serverHello.srtpProtectionProfile != 0 { + if hs.serverHello.srtpMasterKeyIdentifier != "" { + return false, errors.New("tls: server selected SRTP MKI value") + } + + found := false + for _, p := range c.config.SRTPProtectionProfiles { + if p == hs.serverHello.srtpProtectionProfile { + found = true + break + } + } + if !found { + return false, errors.New("tls: server advertised unsupported SRTP profile") + } + + c.srtpProtectionProfile = hs.serverHello.srtpProtectionProfile + } + + if hs.serverResumedSession() { + // Restore masterSecret and peerCerts from previous state + hs.masterSecret = hs.session.masterSecret + c.peerCertificates = hs.session.serverCertificates + c.extendedMasterSecret = hs.session.extendedMasterSecret + hs.finishedHash.discardHandshakeBuffer() + return true, nil + } + return false, nil +} + +func (hs *clientHandshakeState) readFinished() 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...) + 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.server.Sum(nil), + serverCertificates: c.peerCertificates, + } + + if !hs.serverHello.ticketSupported { + if hs.session == nil && len(hs.serverHello.sessionId) > 0 { + session.sessionId = hs.serverHello.sessionId + hs.session = session + } + return nil + } + + 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(isResume bool) error { + c := hs.c + + var postCCSBytes []byte + seqno := hs.c.sendHandshakeSeq + if hs.serverHello.nextProtoNeg { + nextProto := new(nextProtoMsg) + proto, fallback := mutualProtocol(c.config.NextProtos, hs.serverHello.nextProtos) + nextProto.proto = proto + c.clientProtocol = proto + c.clientProtocolFallback = fallback + + nextProtoBytes := nextProto.marshal() + hs.writeHash(nextProtoBytes, seqno) + seqno++ + postCCSBytes = append(postCCSBytes, nextProtoBytes...) + } + + if hs.serverHello.channelIDRequested { + encryptedExtensions := new(encryptedExtensionsMsg) + if c.config.ChannelID.Curve != elliptic.P256() { + return fmt.Errorf("tls: Channel ID is not on P-256.") + } + var resumeHash []byte + if isResume { + resumeHash = hs.session.handshakeHash + } + r, s, err := ecdsa.Sign(c.config.rand(), c.config.ChannelID, hs.finishedHash.hashForChannelID(resumeHash)) + if err != nil { + return 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) + encryptedExtensions.channelID = channelID + + c.channelID = &c.config.ChannelID.PublicKey + + encryptedExtensionsBytes := encryptedExtensions.marshal() + hs.writeHash(encryptedExtensionsBytes, seqno) + seqno++ + postCCSBytes = append(postCCSBytes, encryptedExtensionsBytes...) + } + + finished := new(finishedMsg) + if c.config.Bugs.EarlyChangeCipherSpec == 2 { + finished.verifyData = hs.finishedHash.clientSum(nil) + } else { + finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret) + } + c.clientVerify = append(c.clientVerify[:0], finished.verifyData...) + finishedBytes := finished.marshal() + hs.writeHash(finishedBytes, seqno) + postCCSBytes = append(postCCSBytes, finishedBytes...) + + if c.config.Bugs.FragmentAcrossChangeCipherSpec { + c.writeRecord(recordTypeHandshake, postCCSBytes[:5]) + postCCSBytes = postCCSBytes[5:] + } + + if !c.config.Bugs.SkipChangeCipherSpec && + c.config.Bugs.EarlyChangeCipherSpec == 0 { + c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + } + + if c.config.Bugs.AppDataAfterChangeCipherSpec != nil { + c.writeRecord(recordTypeApplicationData, c.config.Bugs.AppDataAfterChangeCipherSpec) + } + + c.writeRecord(recordTypeHandshake, postCCSBytes) + return 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) + } +} + +// 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) +} diff --git a/src/ssl/test/runner/handshake_messages.go b/src/ssl/test/runner/handshake_messages.go new file mode 100644 index 0000000..ce214fd --- /dev/null +++ b/src/ssl/test/runner/handshake_messages.go @@ -0,0 +1,1875 @@ +// 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 main + +import "bytes" + +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 + ticketSupported bool + sessionTicket []uint8 + signatureAndHashes []signatureAndHash + secureRenegotiation []byte + alpnProtocols []string + duplicateExtension bool + channelIDSupported bool + npnLast bool + extendedMasterSecret bool + srtpProtectionProfiles []uint16 + srtpMasterKeyIdentifier string + sctListSupported 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.ticketSupported == m1.ticketSupported && + bytes.Equal(m.sessionTicket, m1.sessionTicket) && + eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) && + 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.npnLast == m1.npnLast && + m.extendedMasterSecret == m1.extendedMasterSecret && + eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) && + m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier && + m.sctListSupported == m1.sctListSupported +} + +func (m *clientHelloMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + length := 2 + 32 + 1 + len(m.sessionId) + 2 + len(m.cipherSuites)*2 + 1 + len(m.compressionMethods) + if m.isDTLS { + length += 1 + len(m.cookie) + } + numExtensions := 0 + extensionsLength := 0 + if m.nextProtoNeg { + numExtensions++ + } + if m.ocspStapling { + extensionsLength += 1 + 2 + 2 + numExtensions++ + } + if len(m.serverName) > 0 { + extensionsLength += 5 + len(m.serverName) + numExtensions++ + } + if len(m.supportedCurves) > 0 { + extensionsLength += 2 + 2*len(m.supportedCurves) + numExtensions++ + } + if len(m.supportedPoints) > 0 { + extensionsLength += 1 + len(m.supportedPoints) + numExtensions++ + } + if m.ticketSupported { + extensionsLength += len(m.sessionTicket) + numExtensions++ + } + if len(m.signatureAndHashes) > 0 { + extensionsLength += 2 + 2*len(m.signatureAndHashes) + numExtensions++ + } + if m.secureRenegotiation != nil { + extensionsLength += 1 + len(m.secureRenegotiation) + numExtensions++ + } + if m.duplicateExtension { + numExtensions += 2 + } + if m.channelIDSupported { + numExtensions++ + } + if len(m.alpnProtocols) > 0 { + extensionsLength += 2 + for _, s := range m.alpnProtocols { + if l := len(s); l == 0 || l > 255 { + panic("invalid ALPN protocol") + } + extensionsLength++ + extensionsLength += len(s) + } + numExtensions++ + } + if m.extendedMasterSecret { + numExtensions++ + } + if len(m.srtpProtectionProfiles) > 0 { + extensionsLength += 2 + 2*len(m.srtpProtectionProfiles) + extensionsLength += 1 + len(m.srtpMasterKeyIdentifier) + numExtensions++ + } + if m.sctListSupported { + numExtensions++ + } + if numExtensions > 0 { + extensionsLength += 4 * numExtensions + length += 2 + extensionsLength + } + + x := make([]byte, 4+length) + x[0] = typeClientHello + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + vers := versionToWire(m.vers, m.isDTLS) + x[4] = uint8(vers >> 8) + x[5] = uint8(vers) + copy(x[6:38], m.random) + x[38] = uint8(len(m.sessionId)) + copy(x[39:39+len(m.sessionId)], m.sessionId) + y := x[39+len(m.sessionId):] + if m.isDTLS { + y[0] = uint8(len(m.cookie)) + copy(y[1:], m.cookie) + y = y[1+len(m.cookie):] + } + y[0] = uint8(len(m.cipherSuites) >> 7) + y[1] = uint8(len(m.cipherSuites) << 1) + for i, suite := range m.cipherSuites { + y[2+i*2] = uint8(suite >> 8) + y[3+i*2] = uint8(suite) + } + z := y[2+len(m.cipherSuites)*2:] + z[0] = uint8(len(m.compressionMethods)) + copy(z[1:], m.compressionMethods) + + z = z[1+len(m.compressionMethods):] + if numExtensions > 0 { + z[0] = byte(extensionsLength >> 8) + z[1] = byte(extensionsLength) + z = z[2:] + } + if m.duplicateExtension { + // Add a duplicate bogus extension at the beginning and end. + z[0] = 0xff + z[1] = 0xff + z = z[4:] + } + if m.nextProtoNeg && !m.npnLast { + z[0] = byte(extensionNextProtoNeg >> 8) + z[1] = byte(extensionNextProtoNeg & 0xff) + // The length is always 0 + z = z[4:] + } + if len(m.serverName) > 0 { + z[0] = byte(extensionServerName >> 8) + z[1] = byte(extensionServerName & 0xff) + l := len(m.serverName) + 5 + z[2] = byte(l >> 8) + z[3] = byte(l) + z = z[4:] + + // 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; + + z[0] = byte((len(m.serverName) + 3) >> 8) + z[1] = byte(len(m.serverName) + 3) + z[3] = byte(len(m.serverName) >> 8) + z[4] = byte(len(m.serverName)) + copy(z[5:], []byte(m.serverName)) + z = z[l:] + } + if m.ocspStapling { + // RFC 4366, section 3.6 + z[0] = byte(extensionStatusRequest >> 8) + z[1] = byte(extensionStatusRequest) + z[2] = 0 + z[3] = 5 + z[4] = 1 // OCSP type + // Two zero valued uint16s for the two lengths. + z = z[9:] + } + if len(m.supportedCurves) > 0 { + // http://tools.ietf.org/html/rfc4492#section-5.5.1 + z[0] = byte(extensionSupportedCurves >> 8) + z[1] = byte(extensionSupportedCurves) + l := 2 + 2*len(m.supportedCurves) + z[2] = byte(l >> 8) + z[3] = byte(l) + l -= 2 + z[4] = byte(l >> 8) + z[5] = byte(l) + z = z[6:] + for _, curve := range m.supportedCurves { + z[0] = byte(curve >> 8) + z[1] = byte(curve) + z = z[2:] + } + } + if len(m.supportedPoints) > 0 { + // http://tools.ietf.org/html/rfc4492#section-5.5.2 + z[0] = byte(extensionSupportedPoints >> 8) + z[1] = byte(extensionSupportedPoints) + l := 1 + len(m.supportedPoints) + z[2] = byte(l >> 8) + z[3] = byte(l) + l-- + z[4] = byte(l) + z = z[5:] + for _, pointFormat := range m.supportedPoints { + z[0] = byte(pointFormat) + z = z[1:] + } + } + if m.ticketSupported { + // http://tools.ietf.org/html/rfc5077#section-3.2 + z[0] = byte(extensionSessionTicket >> 8) + z[1] = byte(extensionSessionTicket) + l := len(m.sessionTicket) + z[2] = byte(l >> 8) + z[3] = byte(l) + z = z[4:] + copy(z, m.sessionTicket) + z = z[len(m.sessionTicket):] + } + if len(m.signatureAndHashes) > 0 { + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + z[0] = byte(extensionSignatureAlgorithms >> 8) + z[1] = byte(extensionSignatureAlgorithms) + l := 2 + 2*len(m.signatureAndHashes) + z[2] = byte(l >> 8) + z[3] = byte(l) + z = z[4:] + + l -= 2 + z[0] = byte(l >> 8) + z[1] = byte(l) + z = z[2:] + for _, sigAndHash := range m.signatureAndHashes { + z[0] = sigAndHash.hash + z[1] = sigAndHash.signature + z = z[2:] + } + } + if m.secureRenegotiation != nil { + z[0] = byte(extensionRenegotiationInfo >> 8) + z[1] = byte(extensionRenegotiationInfo & 0xff) + z[2] = 0 + z[3] = byte(1 + len(m.secureRenegotiation)) + z[4] = byte(len(m.secureRenegotiation)) + z = z[5:] + copy(z, m.secureRenegotiation) + z = z[len(m.secureRenegotiation):] + } + if len(m.alpnProtocols) > 0 { + z[0] = byte(extensionALPN >> 8) + z[1] = byte(extensionALPN & 0xff) + lengths := z[2:] + z = z[6:] + + stringsLength := 0 + for _, s := range m.alpnProtocols { + l := len(s) + z[0] = byte(l) + copy(z[1:], s) + z = z[1+l:] + stringsLength += 1 + l + } + + lengths[2] = byte(stringsLength >> 8) + lengths[3] = byte(stringsLength) + stringsLength += 2 + lengths[0] = byte(stringsLength >> 8) + lengths[1] = byte(stringsLength) + } + if m.channelIDSupported { + z[0] = byte(extensionChannelID >> 8) + z[1] = byte(extensionChannelID & 0xff) + z = z[4:] + } + if m.nextProtoNeg && m.npnLast { + z[0] = byte(extensionNextProtoNeg >> 8) + z[1] = byte(extensionNextProtoNeg & 0xff) + // The length is always 0 + z = z[4:] + } + if m.duplicateExtension { + // Add a duplicate bogus extension at the beginning and end. + z[0] = 0xff + z[1] = 0xff + z = z[4:] + } + if m.extendedMasterSecret { + // https://tools.ietf.org/html/draft-ietf-tls-session-hash-01 + z[0] = byte(extensionExtendedMasterSecret >> 8) + z[1] = byte(extensionExtendedMasterSecret & 0xff) + z = z[4:] + } + if len(m.srtpProtectionProfiles) > 0 { + z[0] = byte(extensionUseSRTP >> 8) + z[1] = byte(extensionUseSRTP & 0xff) + + profilesLen := 2 * len(m.srtpProtectionProfiles) + mkiLen := len(m.srtpMasterKeyIdentifier) + l := 2 + profilesLen + 1 + mkiLen + z[2] = byte(l >> 8) + z[3] = byte(l & 0xff) + + z[4] = byte(profilesLen >> 8) + z[5] = byte(profilesLen & 0xff) + z = z[6:] + for _, p := range m.srtpProtectionProfiles { + z[0] = byte(p >> 8) + z[1] = byte(p & 0xff) + z = z[2:] + } + + z[0] = byte(mkiLen) + copy(z[1:], []byte(m.srtpMasterKeyIdentifier)) + z = z[1+mkiLen:] + } + if m.sctListSupported { + z[0] = byte(extensionSignedCertificateTimestamp >> 8) + z[1] = byte(extensionSignedCertificateTimestamp & 0xff) + z = z[4:] + } + + m.raw = x + + return x +} + +func (m *clientHelloMsg) unmarshal(data []byte) bool { + if len(data) < 42 { + return false + } + m.raw = data + m.vers = wireToVersion(uint16(data[4])<<8|uint16(data[5]), m.isDTLS) + 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.ticketSupported = false + m.sessionTicket = nil + m.signatureAndHashes = nil + m.alpnProtocols = nil + m.extendedMasterSecret = false + + 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 = make([]uint8, l) + copy(m.supportedPoints, data[1:]) + case extensionSessionTicket: + // http://tools.ietf.org/html/rfc5077#section-3.2 + m.ticketSupported = true + m.sessionTicket = data[:length] + 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.signatureAndHashes = make([]signatureAndHash, n) + for i := range m.signatureAndHashes { + m.signatureAndHashes[i].hash = d[0] + m.signatureAndHashes[i].signature = 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 + } + data = data[length:] + } + + return true +} + +type serverHelloMsg struct { + raw []byte + isDTLS bool + vers uint16 + random []byte + sessionId []byte + cipherSuite uint16 + compressionMethod uint8 + nextProtoNeg bool + nextProtos []string + ocspStapling bool + ticketSupported bool + secureRenegotiation []byte + alpnProtocol string + duplicateExtension bool + channelIDRequested bool + extendedMasterSecret bool + srtpProtectionProfile uint16 + srtpMasterKeyIdentifier string + sctList []byte +} + +func (m *serverHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*serverHelloMsg) + 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) && + m.cipherSuite == m1.cipherSuite && + m.compressionMethod == m1.compressionMethod && + m.nextProtoNeg == m1.nextProtoNeg && + eqStrings(m.nextProtos, m1.nextProtos) && + m.ocspStapling == m1.ocspStapling && + m.ticketSupported == m1.ticketSupported && + bytes.Equal(m.secureRenegotiation, m1.secureRenegotiation) && + (m.secureRenegotiation == nil) == (m1.secureRenegotiation == nil) && + m.alpnProtocol == m1.alpnProtocol && + m.duplicateExtension == m1.duplicateExtension && + m.channelIDRequested == m1.channelIDRequested && + m.extendedMasterSecret == m1.extendedMasterSecret && + m.srtpProtectionProfile == m1.srtpProtectionProfile && + m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier && + bytes.Equal(m.sctList, m1.sctList) +} + +func (m *serverHelloMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + length := 38 + len(m.sessionId) + numExtensions := 0 + extensionsLength := 0 + + nextProtoLen := 0 + if m.nextProtoNeg { + numExtensions++ + for _, v := range m.nextProtos { + nextProtoLen += len(v) + } + nextProtoLen += len(m.nextProtos) + extensionsLength += nextProtoLen + } + if m.ocspStapling { + numExtensions++ + } + if m.ticketSupported { + numExtensions++ + } + if m.secureRenegotiation != nil { + extensionsLength += 1 + len(m.secureRenegotiation) + numExtensions++ + } + if m.duplicateExtension { + numExtensions += 2 + } + if m.channelIDRequested { + numExtensions++ + } + if alpnLen := len(m.alpnProtocol); alpnLen > 0 { + if alpnLen >= 256 { + panic("invalid ALPN protocol") + } + extensionsLength += 2 + 1 + alpnLen + numExtensions++ + } + if m.extendedMasterSecret { + numExtensions++ + } + if m.srtpProtectionProfile != 0 { + extensionsLength += 2 + 2 + 1 + len(m.srtpMasterKeyIdentifier) + numExtensions++ + } + if m.sctList != nil { + extensionsLength += len(m.sctList) + numExtensions++ + } + + if numExtensions > 0 { + extensionsLength += 4 * numExtensions + length += 2 + extensionsLength + } + + x := make([]byte, 4+length) + x[0] = typeServerHello + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + vers := versionToWire(m.vers, m.isDTLS) + x[4] = uint8(vers >> 8) + x[5] = uint8(vers) + copy(x[6:38], m.random) + x[38] = uint8(len(m.sessionId)) + copy(x[39:39+len(m.sessionId)], m.sessionId) + z := x[39+len(m.sessionId):] + z[0] = uint8(m.cipherSuite >> 8) + z[1] = uint8(m.cipherSuite) + z[2] = uint8(m.compressionMethod) + + z = z[3:] + if numExtensions > 0 { + z[0] = byte(extensionsLength >> 8) + z[1] = byte(extensionsLength) + z = z[2:] + } + if m.duplicateExtension { + // Add a duplicate bogus extension at the beginning and end. + z[0] = 0xff + z[1] = 0xff + z = z[4:] + } + if m.nextProtoNeg { + z[0] = byte(extensionNextProtoNeg >> 8) + z[1] = byte(extensionNextProtoNeg & 0xff) + z[2] = byte(nextProtoLen >> 8) + z[3] = byte(nextProtoLen) + z = z[4:] + + for _, v := range m.nextProtos { + l := len(v) + if l > 255 { + l = 255 + } + z[0] = byte(l) + copy(z[1:], []byte(v[0:l])) + z = z[1+l:] + } + } + if m.ocspStapling { + z[0] = byte(extensionStatusRequest >> 8) + z[1] = byte(extensionStatusRequest) + z = z[4:] + } + if m.ticketSupported { + z[0] = byte(extensionSessionTicket >> 8) + z[1] = byte(extensionSessionTicket) + z = z[4:] + } + if m.secureRenegotiation != nil { + z[0] = byte(extensionRenegotiationInfo >> 8) + z[1] = byte(extensionRenegotiationInfo & 0xff) + z[2] = 0 + z[3] = byte(1 + len(m.secureRenegotiation)) + z[4] = byte(len(m.secureRenegotiation)) + z = z[5:] + copy(z, m.secureRenegotiation) + z = z[len(m.secureRenegotiation):] + } + if alpnLen := len(m.alpnProtocol); alpnLen > 0 { + z[0] = byte(extensionALPN >> 8) + z[1] = byte(extensionALPN & 0xff) + l := 2 + 1 + alpnLen + z[2] = byte(l >> 8) + z[3] = byte(l) + l -= 2 + z[4] = byte(l >> 8) + z[5] = byte(l) + l -= 1 + z[6] = byte(l) + copy(z[7:], []byte(m.alpnProtocol)) + z = z[7+alpnLen:] + } + if m.channelIDRequested { + z[0] = byte(extensionChannelID >> 8) + z[1] = byte(extensionChannelID & 0xff) + z = z[4:] + } + if m.duplicateExtension { + // Add a duplicate bogus extension at the beginning and end. + z[0] = 0xff + z[1] = 0xff + z = z[4:] + } + if m.extendedMasterSecret { + z[0] = byte(extensionExtendedMasterSecret >> 8) + z[1] = byte(extensionExtendedMasterSecret & 0xff) + z = z[4:] + } + if m.srtpProtectionProfile != 0 { + z[0] = byte(extensionUseSRTP >> 8) + z[1] = byte(extensionUseSRTP & 0xff) + l := 2 + 2 + 1 + len(m.srtpMasterKeyIdentifier) + z[2] = byte(l >> 8) + z[3] = byte(l & 0xff) + z[4] = 0 + z[5] = 2 + z[6] = byte(m.srtpProtectionProfile >> 8) + z[7] = byte(m.srtpProtectionProfile & 0xff) + l = len(m.srtpMasterKeyIdentifier) + z[8] = byte(l) + copy(z[9:], []byte(m.srtpMasterKeyIdentifier)) + z = z[9+l:] + } + if m.sctList != nil { + z[0] = byte(extensionSignedCertificateTimestamp >> 8) + z[1] = byte(extensionSignedCertificateTimestamp & 0xff) + l := len(m.sctList) + z[2] = byte(l >> 8) + z[3] = byte(l & 0xff) + copy(z[4:], m.sctList) + z = z[4+l:] + } + + m.raw = x + + return x +} + +func (m *serverHelloMsg) unmarshal(data []byte) bool { + if len(data) < 42 { + return false + } + m.raw = data + m.vers = wireToVersion(uint16(data[4])<<8|uint16(data[5]), m.isDTLS) + 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 len(data) < 3 { + return false + } + m.cipherSuite = uint16(data[0])<<8 | uint16(data[1]) + m.compressionMethod = data[2] + data = data[3:] + + m.nextProtoNeg = false + m.nextProtos = nil + m.ocspStapling = false + m.ticketSupported = false + m.alpnProtocol = "" + m.extendedMasterSecret = false + + if len(data) == 0 { + // ServerHello 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 len(data) != extensionsLength { + 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 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) + 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: + if length < 2 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l != len(data)-2 { + return false + } + m.sctList = data[2:length] + } + data = data[length:] + } + + return true +} + +type certificateMsg struct { + raw []byte + certificates [][]byte +} + +func (m *certificateMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + eqByteSlices(m.certificates, m1.certificates) +} + +func (m *certificateMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + var i int + for _, slice := range m.certificates { + i += len(slice) + } + + length := 3 + 3*len(m.certificates) + i + x = make([]byte, 4+length) + x[0] = typeCertificate + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + + certificateOctets := length - 3 + x[4] = uint8(certificateOctets >> 16) + x[5] = uint8(certificateOctets >> 8) + x[6] = uint8(certificateOctets) + + y := x[7:] + for _, slice := range m.certificates { + y[0] = uint8(len(slice) >> 16) + y[1] = uint8(len(slice) >> 8) + y[2] = uint8(len(slice)) + copy(y[3:], slice) + y = y[3+len(slice):] + } + + m.raw = x + return +} + +func (m *certificateMsg) unmarshal(data []byte) bool { + if len(data) < 7 { + return false + } + + m.raw = data + certsLen := uint32(data[4])<<16 | uint32(data[5])<<8 | uint32(data[6]) + if uint32(len(data)) != certsLen+7 { + return false + } + + numCerts := 0 + d := data[7:] + for certsLen > 0 { + if len(d) < 4 { + return false + } + certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) + if uint32(len(d)) < 3+certLen { + return false + } + d = d[3+certLen:] + certsLen -= 3 + certLen + numCerts++ + } + + m.certificates = make([][]byte, numCerts) + d = data[7:] + for i := 0; i < numCerts; i++ { + certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2]) + m.certificates[i] = d[3 : 3+certLen] + d = d[3+certLen:] + } + + return true +} + +type serverKeyExchangeMsg struct { + raw []byte + key []byte +} + +func (m *serverKeyExchangeMsg) equal(i interface{}) bool { + m1, ok := i.(*serverKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.key, m1.key) +} + +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) equal(i interface{}) bool { + m1, ok := i.(*certificateStatusMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.statusType == m1.statusType && + bytes.Equal(m.response, m1.response) +} + +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) equal(i interface{}) bool { + _, ok := i.(*serverHelloDoneMsg) + return ok +} + +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) equal(i interface{}) bool { + m1, ok := i.(*clientKeyExchangeMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.ciphertext, m1.ciphertext) +} + +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) equal(i interface{}) bool { + m1, ok := i.(*finishedMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.verifyData, m1.verifyData) +} + +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) equal(i interface{}) bool { + m1, ok := i.(*nextProtoMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.proto == m1.proto +} + +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 + // hasSignatureAndHash indicates whether this message includes a list + // of signature and hash functions. This change was introduced with TLS + // 1.2. + hasSignatureAndHash bool + + certificateTypes []byte + signatureAndHashes []signatureAndHash + certificateAuthorities [][]byte +} + +func (m *certificateRequestMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateRequestMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.certificateTypes, m1.certificateTypes) && + eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) && + eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) +} + +func (m *certificateRequestMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See http://tools.ietf.org/html/rfc4346#section-7.4.4 + length := 1 + len(m.certificateTypes) + 2 + casLength := 0 + for _, ca := range m.certificateAuthorities { + casLength += 2 + len(ca) + } + length += casLength + + if m.hasSignatureAndHash { + length += 2 + 2*len(m.signatureAndHashes) + } + + x = make([]byte, 4+length) + x[0] = typeCertificateRequest + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + + x[4] = uint8(len(m.certificateTypes)) + + copy(x[5:], m.certificateTypes) + y := x[5+len(m.certificateTypes):] + + if m.hasSignatureAndHash { + n := len(m.signatureAndHashes) * 2 + y[0] = uint8(n >> 8) + y[1] = uint8(n) + y = y[2:] + for _, sigAndHash := range m.signatureAndHashes { + y[0] = sigAndHash.hash + y[1] = sigAndHash.signature + y = y[2:] + } + } + + y[0] = uint8(casLength >> 8) + y[1] = uint8(casLength) + y = y[2:] + for _, ca := range m.certificateAuthorities { + y[0] = uint8(len(ca) >> 8) + y[1] = uint8(len(ca)) + y = y[2:] + copy(y, ca) + y = y[len(ca):] + } + + m.raw = x + return +} + +func (m *certificateRequestMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 5 { + return false + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return false + } + + numCertTypes := int(data[4]) + data = data[5:] + if numCertTypes == 0 || len(data) <= numCertTypes { + return false + } + + m.certificateTypes = make([]byte, numCertTypes) + if copy(m.certificateTypes, data) != numCertTypes { + return false + } + + data = data[numCertTypes:] + + if m.hasSignatureAndHash { + if len(data) < 2 { + return false + } + sigAndHashLen := uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + if sigAndHashLen&1 != 0 { + return false + } + if len(data) < int(sigAndHashLen) { + return false + } + numSigAndHash := sigAndHashLen / 2 + m.signatureAndHashes = make([]signatureAndHash, numSigAndHash) + for i := range m.signatureAndHashes { + m.signatureAndHashes[i].hash = data[0] + m.signatureAndHashes[i].signature = 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 len(data) > 0 { + return false + } + + return true +} + +type certificateVerifyMsg struct { + raw []byte + hasSignatureAndHash bool + signatureAndHash signatureAndHash + signature []byte +} + +func (m *certificateVerifyMsg) equal(i interface{}) bool { + m1, ok := i.(*certificateVerifyMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.hasSignatureAndHash == m1.hasSignatureAndHash && + m.signatureAndHash.hash == m1.signatureAndHash.hash && + m.signatureAndHash.signature == m1.signatureAndHash.signature && + bytes.Equal(m.signature, m1.signature) +} + +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.hasSignatureAndHash { + 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.hasSignatureAndHash { + y[0] = m.signatureAndHash.hash + y[1] = m.signatureAndHash.signature + 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.hasSignatureAndHash { + m.signatureAndHash.hash = data[0] + m.signatureAndHash.signature = 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 + ticket []byte +} + +func (m *newSessionTicketMsg) equal(i interface{}) bool { + m1, ok := i.(*newSessionTicketMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.ticket, m1.ticket) +} + +func (m *newSessionTicketMsg) marshal() (x []byte) { + if m.raw != nil { + return m.raw + } + + // See http://tools.ietf.org/html/rfc5077#section-3.3 + ticketLen := len(m.ticket) + length := 2 + 4 + ticketLen + x = make([]byte, 4+length) + x[0] = typeNewSessionTicket + x[1] = uint8(length >> 16) + x[2] = uint8(length >> 8) + x[3] = uint8(length) + x[8] = uint8(ticketLen >> 8) + x[9] = uint8(ticketLen) + copy(x[10:], m.ticket) + + m.raw = x + + return +} + +func (m *newSessionTicketMsg) unmarshal(data []byte) bool { + m.raw = data + + if len(data) < 10 { + return false + } + + length := uint32(data[1])<<16 | uint32(data[2])<<8 | uint32(data[3]) + if uint32(len(data))-4 != length { + return false + } + + ticketLen := int(data[8])<<8 + int(data[9]) + if len(data)-10 != ticketLen { + return false + } + + m.ticket = data[10:] + + return true +} + +type v2ClientHelloMsg struct { + raw []byte + vers uint16 + cipherSuites []uint16 + sessionId []byte + challenge []byte +} + +func (m *v2ClientHelloMsg) equal(i interface{}) bool { + m1, ok := i.(*v2ClientHelloMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + eqUint16s(m.cipherSuites, m1.cipherSuites) && + bytes.Equal(m.sessionId, m1.sessionId) && + bytes.Equal(m.challenge, m1.challenge) +} + +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) equal(i interface{}) bool { + m1, ok := i.(*helloVerifyRequestMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + m.vers == m1.vers && + bytes.Equal(m.cookie, m1.cookie) +} + +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 := versionToWire(m.vers, true) + 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 = wireToVersion(uint16(data[4])<<8|uint16(data[5]), true) + cookieLen := int(data[6]) + if cookieLen > 32 || len(data) != 7+cookieLen { + return false + } + m.cookie = data[7 : 7+cookieLen] + + return true +} + +type encryptedExtensionsMsg struct { + raw []byte + channelID []byte +} + +func (m *encryptedExtensionsMsg) equal(i interface{}) bool { + m1, ok := i.(*encryptedExtensionsMsg) + if !ok { + return false + } + + return bytes.Equal(m.raw, m1.raw) && + bytes.Equal(m.channelID, m1.channelID) +} + +func (m *encryptedExtensionsMsg) marshal() []byte { + if m.raw != nil { + return m.raw + } + + length := 2 + 2 + len(m.channelID) + + x := make([]byte, 4+length) + x[0] = typeEncryptedExtensions + 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 *encryptedExtensionsMsg) 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 +} + +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 eqSignatureAndHashes(x, y []signatureAndHash) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + v2 := y[i] + if v.hash != v2.hash || v.signature != v2.signature { + return false + } + } + return true +} diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go new file mode 100644 index 0000000..1234a57 --- /dev/null +++ b/src/ssl/test/runner/handshake_server.go @@ -0,0 +1,964 @@ +// 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 main + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/subtle" + "crypto/x509" + "encoding/asn1" + "errors" + "fmt" + "io" + "math/big" +) + +// 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 +} + +// 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, + } + isResume, err := hs.readClientHello() + 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(); err != nil { + return err + } + if err := hs.readFinished(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(isResume); err != nil { + return err + } + if c.config.Bugs.ExpectFalseStart { + if err := c.readRecord(recordTypeApplicationData); err != nil { + return err + } + } + if err := hs.sendSessionTicket(); err != nil { + return err + } + if err := hs.sendFinished(); err != nil { + return err + } + } + c.handshakeComplete = true + + return nil +} + +// readClientHello reads a ClientHello message from the client and decides +// whether we will perform session resumption. +func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { + config := hs.c.config + c := hs.c + + msg, err := c.readHandshake() + if err != nil { + return false, err + } + var ok bool + hs.clientHello, ok = msg.(*clientHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return false, unexpectedMessageError(hs.clientHello, msg) + } + if config.Bugs.RequireFastradioPadding && len(hs.clientHello.raw) < 1000 { + return false, errors.New("tls: ClientHello record size should be larger than 1000 bytes when padding enabled.") + } + + if c.isDTLS && !config.Bugs.SkipHelloVerifyRequest { + // Per RFC 6347, the version field in HelloVerifyRequest SHOULD + // be always DTLS 1.0 + helloVerifyRequest := &helloVerifyRequestMsg{ + vers: VersionTLS10, + cookie: make([]byte, 32), + } + if _, err := io.ReadFull(c.config.rand(), helloVerifyRequest.cookie); err != nil { + c.sendAlert(alertInternalError) + return false, errors.New("dtls: short read from Rand: " + err.Error()) + } + c.writeRecord(recordTypeHandshake, helloVerifyRequest.marshal()) + + msg, err := c.readHandshake() + if err != nil { + return false, err + } + newClientHello, ok := msg.(*clientHelloMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return false, unexpectedMessageError(hs.clientHello, msg) + } + if !bytes.Equal(newClientHello.cookie, helloVerifyRequest.cookie) { + return false, 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 false, 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 false, fmt.Errorf("tls: client offered different version on renego") + } + } + c.clientVersion = hs.clientHello.vers + + // Reject < 1.2 ClientHellos with signature_algorithms. + if c.clientVersion < VersionTLS12 && len(hs.clientHello.signatureAndHashes) > 0 { + return false, fmt.Errorf("tls: client included signature_algorithms before TLS 1.2") + } + + c.vers, ok = config.mutualVersion(hs.clientHello.vers) + if !ok { + c.sendAlert(alertProtocolVersion) + return false, fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers) + } + c.haveVers = true + + hs.hello = new(serverHelloMsg) + hs.hello.isDTLS = c.isDTLS + + 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 + + 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") + } + + hs.hello.vers = c.vers + hs.hello.random = make([]byte, 32) + _, err = io.ReadFull(config.rand(), hs.hello.random) + if err != nil { + c.sendAlert(alertInternalError) + return false, err + } + + if !bytes.Equal(c.clientVerify, hs.clientHello.secureRenegotiation) { + c.sendAlert(alertHandshakeFailure) + return false, errors.New("tls: renegotiation mismatch") + } + + if len(c.clientVerify) > 0 && !c.config.Bugs.EmptyRenegotiationInfo { + hs.hello.secureRenegotiation = append(hs.hello.secureRenegotiation, c.clientVerify...) + hs.hello.secureRenegotiation = append(hs.hello.secureRenegotiation, c.serverVerify...) + if c.config.Bugs.BadRenegotiationInfo { + hs.hello.secureRenegotiation[0] ^= 0x80 + } + } else { + hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation + } + + hs.hello.compressionMethod = compressionNone + hs.hello.duplicateExtension = c.config.Bugs.DuplicateExtension + if len(hs.clientHello.serverName) > 0 { + c.serverName = hs.clientHello.serverName + } + + if len(hs.clientHello.alpnProtocols) > 0 { + if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback { + hs.hello.alpnProtocol = selectedProto + c.clientProtocol = selectedProto + c.usedALPN = true + } + } else { + // 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 { + hs.hello.nextProtoNeg = true + hs.hello.nextProtos = config.NextProtos + } + } + hs.hello.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !c.config.Bugs.NoExtendedMasterSecret + + if len(config.Certificates) == 0 { + c.sendAlert(alertInternalError) + return false, 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 false, errors.New("tls: unexpected server name") + } + + if hs.clientHello.channelIDSupported && config.RequestChannelID { + hs.hello.channelIDRequested = true + } + + if hs.clientHello.srtpProtectionProfiles != nil { + SRTPLoop: + for _, p1 := range c.config.SRTPProtectionProfiles { + for _, p2 := range hs.clientHello.srtpProtectionProfiles { + if p1 == p2 { + hs.hello.srtpProtectionProfile = p1 + c.srtpProtectionProfile = p1 + break SRTPLoop + } + } + } + } + + if c.config.Bugs.SendSRTPProtectionProfile != 0 { + hs.hello.srtpProtectionProfile = c.config.Bugs.SendSRTPProtectionProfile + } + + _, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey) + + if hs.checkForResumption() { + return true, nil + } + + var scsvFound bool + + for _, cipherSuite := range hs.clientHello.cipherSuites { + if cipherSuite == fallbackSCSV { + scsvFound = true + break + } + } + + if !scsvFound && config.Bugs.FailIfNotFallbackSCSV { + return false, errors.New("tls: no fallback SCSV found when expected") + } else if scsvFound && !config.Bugs.FailIfNotFallbackSCSV { + return false, errors.New("tls: fallback SCSV found when not expected") + } + + 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 +} + +// checkForResumption returns true if we should perform resumption on this connection. +func (hs *serverHandshakeState) checkForResumption() bool { + c := hs.c + + if len(hs.clientHello.sessionTicket) > 0 { + if c.config.SessionTicketsDisabled { + return false + } + + var ok bool + if hs.sessionState, ok = c.decryptTicket(hs.clientHello.sessionTicket); !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 + } + } + + // Never resume a session for a different SSL version. + if !c.config.Bugs.AllowSessionVersionMismatch && 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(), hs.sessionState.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 + // 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.ticketSupported = c.config.Bugs.RenewTicketOnResume + + 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.ocspStapling = true + } + + if hs.clientHello.sctListSupported && len(hs.cert.SignedCertificateTimestampList) > 0 { + hs.hello.sctList = hs.cert.SignedCertificateTimestampList + } + + hs.hello.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.extendedMasterSecret + + // Generate a session ID if we're to save the session. + if !hs.hello.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()) + + c.writeRecord(recordTypeHandshake, hs.hello.marshal()) + + if !isPSK { + certMsg := new(certificateMsg) + certMsg.certificates = hs.cert.Certificate + if !config.Bugs.UnauthenticatedECDH { + hs.writeServerHash(certMsg.marshal()) + c.writeRecord(recordTypeHandshake, certMsg.marshal()) + } + } + + if hs.hello.ocspStapling { + 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 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.hasSignatureAndHash = true + if !config.Bugs.NoSignatureAndHashes { + certReq.signatureAndHashes = config.signatureAndHashesForServer() + } + } + + // 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()) + + var pub crypto.PublicKey // public key for client auth, if any + + 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 + if certMsg, ok = msg.(*certificateMsg); !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(alertBadCertificate) + return errors.New("tls: client didn't provide a certificate") + } + } + + pub, err = hs.processCertsFromClient(certMsg.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 signatureAndHash signatureAndHash + if certVerify.hasSignatureAndHash { + signatureAndHash = certVerify.signatureAndHash + if !isSupportedSignatureAndHash(signatureAndHash, config.signatureAndHashesForServer()) { + return errors.New("tls: unsupported hash function for client certificate") + } + } else { + // Before TLS 1.2 the signature algorithm was implicit + // from the key type, and only one hash per signature + // algorithm was possible. Leave the hash as zero. + switch pub.(type) { + case *ecdsa.PublicKey: + signatureAndHash.signature = signatureECDSA + case *rsa.PublicKey: + signatureAndHash.signature = signatureRSA + } + } + + switch key := pub.(type) { + case *ecdsa.PublicKey: + if signatureAndHash.signature != signatureECDSA { + err = errors.New("tls: bad signature type for client's ECDSA certificate") + break + } + ecdsaSig := new(ecdsaSignature) + if _, err = asn1.Unmarshal(certVerify.signature, ecdsaSig); err != nil { + break + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + err = errors.New("ECDSA signature contained zero or negative values") + break + } + var digest []byte + digest, _, err = hs.finishedHash.hashForClientCertificate(signatureAndHash, hs.masterSecret) + if err != nil { + break + } + if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) { + err = errors.New("ECDSA verification failure") + break + } + case *rsa.PublicKey: + if signatureAndHash.signature != signatureRSA { + err = errors.New("tls: bad signature type for client's RSA certificate") + break + } + var digest []byte + var hashFunc crypto.Hash + digest, hashFunc, err = hs.finishedHash.hashForClientCertificate(signatureAndHash, hs.masterSecret) + if err != nil { + break + } + err = rsa.VerifyPKCS1v15(key, hashFunc, 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) + + 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(clientKey, clientIV) + serverCipher = hs.suite.aead(serverKey, serverIV) + } + + c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) + c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) + + return nil +} + +func (hs *serverHandshakeState) readFinished(isResume bool) error { + c := hs.c + + c.readRecord(recordTypeChangeCipherSpec) + if err := c.in.error(); err != nil { + return err + } + + if hs.hello.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.channelIDRequested { + msg, err := c.readHandshake() + if err != nil { + return err + } + encryptedExtensions, ok := msg.(*encryptedExtensionsMsg) + if !ok { + c.sendAlert(alertUnexpectedMessage) + return unexpectedMessageError(encryptedExtensions, msg) + } + x := new(big.Int).SetBytes(encryptedExtensions.channelID[0:32]) + y := new(big.Int).SetBytes(encryptedExtensions.channelID[32:64]) + r := new(big.Int).SetBytes(encryptedExtensions.channelID[64:96]) + s := new(big.Int).SetBytes(encryptedExtensions.channelID[96:128]) + if !elliptic.P256().IsOnCurve(x, y) { + return errors.New("tls: invalid channel ID public key") + } + channelID := &ecdsa.PublicKey{elliptic.P256(), x, y} + var resumeHash []byte + if isResume { + resumeHash = hs.sessionState.handshakeHash + } + if !ecdsa.Verify(channelID, hs.finishedHash.hashForChannelID(resumeHash), r, s) { + return errors.New("tls: invalid channel ID signature") + } + c.channelID = channelID + + hs.writeClientHash(encryptedExtensions.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...) + + 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.server.Sum(nil), + } + + if !hs.hello.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) + + 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() error { + c := hs.c + + finished := new(finishedMsg) + finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) + c.serverVerify = append(c.serverVerify[:0], finished.verifyData...) + postCCSBytes := finished.marshal() + hs.writeServerHash(postCCSBytes) + + if c.config.Bugs.FragmentAcrossChangeCipherSpec { + c.writeRecord(recordTypeHandshake, postCCSBytes[:5]) + postCCSBytes = postCCSBytes[5:] + } + + if !c.config.Bugs.SkipChangeCipherSpec { + c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) + } + + if c.config.Bugs.AppDataAfterChangeCipherSpec != nil { + c.writeRecord(recordTypeApplicationData, c.config.Bugs.AppDataAfterChangeCipherSpec) + } + + c.writeRecord(recordTypeHandshake, postCCSBytes) + + c.cipherSuite = hs.suite.id + + 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 (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 (candidate.flags&suiteECDHE != 0) && !ellipticOk { + continue + } + if (candidate.flags&suiteECDSA != 0) != ecdsaOk { + continue + } + if !c.config.Bugs.SkipCipherVersionCheck && version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 { + continue + } + if c.isDTLS && candidate.flags&suiteNoDTLS != 0 { + continue + } + return candidate + } + } + + return nil +} diff --git a/src/ssl/test/runner/key.pem b/src/ssl/test/runner/key.pem new file mode 100644 index 0000000..e9107bf --- /dev/null +++ b/src/ssl/test/runner/key.pem @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXgIBAAKBgQDYK8imMuRi/03z0K1Zi0WnvfFHvwlYeyK9Na6XJYaUoIDAtB92 +kWdGMdAQhLciHnAjkXLI6W15OoV3gA/ElRZ1xUpxTMhjP6PyY5wqT5r6y8FxbiiF +KKAnHmUcrgfVW28tQ+0rkLGMryRtrukXOgXBv7gcrmU7G1jC2a7WqmeI8QIDAQAB +AoGBAIBy09Fd4DOq/Ijp8HeKuCMKTHqTW1xGHshLQ6jwVV2vWZIn9aIgmDsvkjCe +i6ssZvnbjVcwzSoByhjN8ZCf/i15HECWDFFh6gt0P5z0MnChwzZmvatV/FXCT0j+ +WmGNB/gkehKjGXLLcjTb6dRYVJSCZhVuOLLcbWIV10gggJQBAkEA8S8sGe4ezyyZ +m4e9r95g6s43kPqtj5rewTsUxt+2n4eVodD+ZUlCULWVNAFLkYRTBCASlSrm9Xhj +QpmWAHJUkQJBAOVzQdFUaewLtdOJoPCtpYoY1zd22eae8TQEmpGOR11L6kbxLQsk +aMly/DOnOaa82tqAGTdqDEZgSNmCeKKknmECQAvpnY8GUOVAubGR6c+W90iBuQLj +LtFp/9ihd2w/PoDwrHZaoUYVcT4VSfJQog/k7kjE4MYXYWL8eEKg3WTWQNECQQDk +104Wi91Umd1PzF0ijd2jXOERJU1wEKe6XLkYYNHWQAe5l4J4MWj9OdxFXAxIuuR/ +tfDwbqkta4xcux67//khAkEAvvRXLHTaa6VFzTaiiO8SaFsHV3lQyXOtMrBpB5jd +moZWgjHvB2W9Ckn7sDqsPB+U2tyX0joDdQEyuiMECDY8oQ== +-----END RSA PRIVATE KEY----- diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go new file mode 100644 index 0000000..116dfd8 --- /dev/null +++ b/src/ssl/test/runner/key_agreement.go @@ -0,0 +1,776 @@ +// 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 main + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/md5" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "encoding/asn1" + "errors" + "io" + "math/big" +) + +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 { + clientVersion uint16 +} + +func (ka *rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + // Save the client version for comparison later. + ka.clientVersion = versionToWire(clientHello.vers, clientHello.isDTLS) + + if config.Bugs.RSAServerKeyExchange { + // Send an empty ServerKeyExchange message. + return &serverKeyExchangeMsg{}, nil + } + + return nil, 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:] + } + + err = rsa.DecryptPKCS1v15SessionKey(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), 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, errors.New("tls: invalid version in RSA premaster") + } + 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) { + preMasterSecret := make([]byte, 48) + vers := clientHello.vers + if config.Bugs.RsaClientKeyExchangeVersion != 0 { + vers = config.Bugs.RsaClientKeyExchangeVersion + } + vers = versionToWire(vers, clientHello.isDTLS) + preMasterSecret[0] = byte(vers >> 8) + preMasterSecret[1] = byte(vers) + _, err := io.ReadFull(config.rand(), preMasterSecret[2:]) + if err != nil { + return nil, nil, err + } + + encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret) + if err != nil { + return nil, nil, err + } + ckx := new(clientKeyExchangeMsg) + if clientHello.vers != VersionSSL30 && !config.Bugs.SSL3RSAKeyExchange { + 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 +} + +// sha1Hash calculates a SHA1 hash over the given byte slices. +func sha1Hash(slices [][]byte) []byte { + hsha1 := sha1.New() + for _, slice := range slices { + hsha1.Write(slice) + } + return hsha1.Sum(nil) +} + +// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the +// concatenation of an MD5 and SHA1 hash. +func md5SHA1Hash(slices [][]byte) []byte { + md5sha1 := make([]byte, md5.Size+sha1.Size) + hmd5 := md5.New() + for _, slice := range slices { + hmd5.Write(slice) + } + copy(md5sha1, hmd5.Sum(nil)) + copy(md5sha1[md5.Size:], sha1Hash(slices)) + return md5sha1 +} + +// hashForServerKeyExchange hashes the given slices and returns their digest +// and the identifier of the hash function used. The hashFunc argument is only +// used for >= TLS 1.2 and precisely identifies the hash function to use. +func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) { + if version >= VersionTLS12 { + hash, err := lookupTLSHash(hashFunc) + if err != nil { + return nil, 0, err + } + h := hash.New() + for _, slice := range slices { + h.Write(slice) + } + return h.Sum(nil), hash, nil + } + if sigType == signatureECDSA { + return sha1Hash(slices), crypto.SHA1, nil + } + return md5SHA1Hash(slices), crypto.MD5SHA1, nil +} + +// pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a +// ServerKeyExchange given the signature type being used and the client's +// advertized list of supported signature and hash combinations. +func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatureAndHash) (uint8, error) { + if len(clientSignatureAndHashes) == 0 { + // 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 + return hashSHA1, nil + } + + for _, sigAndHash := range clientSignatureAndHashes { + if sigAndHash.signature != sigType { + continue + } + switch sigAndHash.hash { + case hashSHA1, hashSHA256: + return sigAndHash.hash, nil + } + } + + return 0, errors.New("tls: client doesn't support any common hash functions") +} + +func curveForCurveID(id CurveID) (elliptic.Curve, bool) { + switch id { + case CurveP256: + return elliptic.P256(), true + case CurveP384: + return elliptic.P384(), true + case CurveP521: + return elliptic.P521(), 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 { + version uint16 + sigType uint8 +} + +func (ka *signedKeyAgreement) signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) { + var tls12HashId uint8 + var err error + if ka.version >= VersionTLS12 { + if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil { + return nil, err + } + } + + digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, hello.random, params) + if err != nil { + return nil, err + } + + if config.Bugs.InvalidSKXSignature { + digest[0] ^= 0x80 + } + + var sig []byte + switch ka.sigType { + case signatureECDSA: + privKey, ok := cert.PrivateKey.(*ecdsa.PrivateKey) + if !ok { + return nil, errors.New("ECDHE ECDSA requires an ECDSA server private key") + } + r, s, err := ecdsa.Sign(config.rand(), privKey, digest) + if err != nil { + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + } + order := privKey.Curve.Params().N + r = maybeCorruptECDSAValue(r, config.Bugs.BadECDSAR, order) + s = maybeCorruptECDSAValue(s, config.Bugs.BadECDSAS, order) + sig, err = asn1.Marshal(ecdsaSignature{r, s}) + case signatureRSA: + privKey, ok := cert.PrivateKey.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("ECDHE RSA requires a RSA server private key") + } + sig, err = rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest) + if err != nil { + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + } + default: + return nil, errors.New("unknown ECDHE signature algorithm") + } + + skx := new(serverKeyExchangeMsg) + if config.Bugs.UnauthenticatedECDH { + skx.key = params + } else { + sigAndHashLen := 0 + if ka.version >= VersionTLS12 { + sigAndHashLen = 2 + } + skx.key = make([]byte, len(params)+sigAndHashLen+2+len(sig)) + copy(skx.key, params) + k := skx.key[len(params):] + if ka.version >= VersionTLS12 { + k[0] = tls12HashId + k[1] = ka.sigType + 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 { + if len(sig) < 2 { + return errServerKeyExchange + } + + var tls12HashId uint8 + if ka.version >= VersionTLS12 { + // handle SignatureAndHashAlgorithm + var sigAndHash []uint8 + sigAndHash, sig = sig[:2], sig[2:] + if sigAndHash[1] != ka.sigType { + return errServerKeyExchange + } + tls12HashId = sigAndHash[0] + if len(sig) < 2 { + return errServerKeyExchange + } + + if !isSupportedSignatureAndHash(signatureAndHash{ka.sigType, tls12HashId}, config.signatureAndHashesForClient()) { + return errors.New("tls: unsupported hash function for ServerKeyExchange") + } + } + sigLen := int(sig[0])<<8 | int(sig[1]) + if sigLen+2 != len(sig) { + return errServerKeyExchange + } + sig = sig[2:] + + digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, serverHello.random, params) + if err != nil { + return err + } + switch ka.sigType { + case signatureECDSA: + pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey) + if !ok { + return errors.New("ECDHE ECDSA requires a ECDSA server public key") + } + 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") + } + if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) { + return errors.New("ECDSA verification failure") + } + case signatureRSA: + pubKey, ok := cert.PublicKey.(*rsa.PublicKey) + if !ok { + return errors.New("ECDHE RSA requires a RSA server public key") + } + if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { + return err + } + default: + return errors.New("unknown ECDHE signature algorithm") + } + + return nil +} + +// ecdheRSAKeyAgreement 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 + privateKey []byte + curve elliptic.Curve + x, y *big.Int +} + +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 (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") + } + + var x, y *big.Int + var err error + ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand()) + if err != nil { + return nil, err + } + ecdhePublic := elliptic.Marshal(ka.curve, x, y) + + // http://tools.ietf.org/html/rfc4492#section-5.4 + serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic)) + serverECDHParams[0] = 3 // named curve + serverECDHParams[1] = byte(curveid >> 8) + serverECDHParams[2] = byte(curveid) + if config.Bugs.InvalidSKXCurve { + serverECDHParams[2] ^= 0xff + } + serverECDHParams[3] = byte(len(ecdhePublic)) + copy(serverECDHParams[4:], ecdhePublic) + + 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 + } + x, y := elliptic.Unmarshal(ka.curve, ckx.ciphertext[1:]) + if x == nil { + return nil, errClientKeyExchange + } + x, _ = ka.curve.ScalarMult(x, y, ka.privateKey) + preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3) + xBytes := x.Bytes() + copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) + + return preMasterSecret, nil +} + +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]) + + 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 + } + ka.x, ka.y = elliptic.Unmarshal(ka.curve, skx.key[4:4+publicLen]) + if ka.x == nil { + return errServerKeyExchange + } + 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") + } + priv, mx, my, err := elliptic.GenerateKey(ka.curve, config.rand()) + if err != nil { + return nil, nil, err + } + x, _ := ka.curve.ScalarMult(ka.x, ka.y, priv) + preMasterSecret := make([]byte, (ka.curve.Params().BitSize+7)>>3) + xBytes := x.Bytes() + copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes) + + serialized := elliptic.Marshal(ka.curve, mx, my) + + ckx := new(clientKeyExchangeMsg) + ckx.ciphertext = make([]byte, 1+len(serialized)) + ckx.ciphertext[0] = byte(len(serialized)) + copy(ckx.ciphertext[1:], serialized) + + return preMasterSecret, ckx, nil +} + +// 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) { + // 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 + } + + 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 +} + +// 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 +} + +// 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 == "" { + // 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 +} diff --git a/src/ssl/test/runner/packet_adapter.go b/src/ssl/test/runner/packet_adapter.go new file mode 100644 index 0000000..671b413 --- /dev/null +++ b/src/ssl/test/runner/packet_adapter.go @@ -0,0 +1,101 @@ +// 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 main + +import ( + "encoding/binary" + "errors" + "net" +) + +type packetAdaptor struct { + net.Conn +} + +// newPacketAdaptor wraps a reliable streaming net.Conn into a +// reliable packet-based net.Conn. Every packet is encoded with a +// 32-bit length prefix as a framing layer. +func newPacketAdaptor(conn net.Conn) net.Conn { + return &packetAdaptor{conn} +} + +func (p *packetAdaptor) Read(b []byte) (int, error) { + var length uint32 + if err := binary.Read(p.Conn, binary.BigEndian, &length); err != nil { + return 0, err + } + out := make([]byte, length) + n, err := p.Conn.Read(out) + if err != nil { + return 0, err + } + if n != int(length) { + return 0, errors.New("internal error: length mismatch!") + } + return copy(b, out), nil +} + +func (p *packetAdaptor) Write(b []byte) (int, error) { + length := uint32(len(b)) + if err := binary.Write(p.Conn, binary.BigEndian, length); err != nil { + return 0, err + } + n, err := p.Conn.Write(b) + if err != nil { + return 0, err + } + if n != len(b) { + return 0, errors.New("internal error: length mismatch!") + } + return len(b), nil +} + +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/src/ssl/test/runner/prf.go b/src/ssl/test/runner/prf.go new file mode 100644 index 0000000..75a8933 --- /dev/null +++ b/src/ssl/test/runner/prf.go @@ -0,0 +1,388 @@ +// 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 main + +import ( + "crypto" + "crypto/hmac" + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "errors" + "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 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: + if suite.flags&suiteSHA384 != 0 { + return prf12(sha512.New384) + } + return prf12(sha256.New) + default: + 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/draft-ietf-tls-session-hash-01 +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 +} + +// lookupTLSHash looks up the corresponding crypto.Hash for a given +// TLS hash identifier. +func lookupTLSHash(hash uint8) (crypto.Hash, error) { + switch hash { + case hashMD5: + return crypto.MD5, nil + case hashSHA1: + return crypto.SHA1, nil + case hashSHA224: + return crypto.SHA224, nil + case hashSHA256: + return crypto.SHA256, nil + case hashSHA384: + return crypto.SHA384, nil + case hashSHA512: + return crypto.SHA512, nil + default: + return 0, errors.New("tls: unsupported hash algorithm") + } +} + +func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash { + if version >= VersionTLS12 { + newHash := sha256.New + if cipherSuite.flags&suiteSHA384 != 0 { + newHash = sha512.New384 + } + + return finishedHash{newHash(), newHash(), nil, nil, []byte{}, version, prf12(newHash)} + } + return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), []byte{}, version, prf10} +} + +// A finishedHash calculates the hash of a set of handshake messages suitable +// for including in a Finished message. +type finishedHash struct { + 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) +} + +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(masterSecret []byte) []byte { + if h.version == VersionSSL30 { + return finishedSum30(h.clientMD5, h.client, masterSecret, ssl3ClientFinishedMagic[:]) + } + + out := make([]byte, finishedVerifyLength) + h.prf(out, masterSecret, clientFinishedLabel, h.Sum()) + return out +} + +// serverSum returns the contents of the verify_data member of a server's +// Finished message. +func (h finishedHash) serverSum(masterSecret []byte) []byte { + if h.version == VersionSSL30 { + return finishedSum30(h.serverMD5, h.server, masterSecret, ssl3ServerFinishedMagic[:]) + } + + out := make([]byte, finishedVerifyLength) + h.prf(out, masterSecret, serverFinishedLabel, h.Sum()) + return out +} + +// selectClientCertSignatureAlgorithm returns a signatureAndHash to sign a +// client's CertificateVerify with, or an error if none can be found. +func (h finishedHash) selectClientCertSignatureAlgorithm(serverList []signatureAndHash, sigType uint8) (signatureAndHash, error) { + if h.version < VersionTLS12 { + // Nothing to negotiate before TLS 1.2. + return signatureAndHash{signature: sigType}, nil + } + + for _, v := range serverList { + if v.signature == sigType && v.hash == hashSHA256 { + return v, nil + } + } + return signatureAndHash{}, errors.New("tls: no supported signature algorithm found for signing client certificate") +} + +// hashForClientCertificate returns a digest, hash function, and TLS 1.2 hash +// id suitable for signing by a TLS client certificate. +func (h finishedHash) hashForClientCertificate(signatureAndHash signatureAndHash, masterSecret []byte) ([]byte, crypto.Hash, error) { + if h.version == VersionSSL30 { + if signatureAndHash.signature != signatureRSA { + return nil, 0, errors.New("tls: unsupported signature type for client certificate") + } + + md5Hash := md5.New() + md5Hash.Write(h.buffer) + sha1Hash := sha1.New() + sha1Hash.Write(h.buffer) + return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), crypto.MD5SHA1, nil + } + if h.version >= VersionTLS12 { + hashAlg, err := lookupTLSHash(signatureAndHash.hash) + if err != nil { + return nil, 0, err + } + hash := hashAlg.New() + hash.Write(h.buffer) + return hash.Sum(nil), hashAlg, nil + } + if signatureAndHash.signature == signatureECDSA { + return h.server.Sum(nil), crypto.SHA1, nil + } + + return h.Sum(), crypto.MD5SHA1, 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.server.Sum(nil)) + 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 +} diff --git a/src/ssl/test/runner/recordingconn.go b/src/ssl/test/runner/recordingconn.go new file mode 100644 index 0000000..a67fa48 --- /dev/null +++ b/src/ssl/test/runner/recordingconn.go @@ -0,0 +1,130 @@ +package main + +import ( + "bufio" + "encoding/hex" + "errors" + "fmt" + "io" + "net" + "strconv" + "strings" + "sync" +) + +// 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 [][]byte + reading bool +} + +func (r *recordingConn) Read(b []byte) (n int, err error) { + if n, err = r.Conn.Read(b); n == 0 { + return + } + b = b[:n] + + r.Lock() + defer r.Unlock() + + if l := len(r.flows); l == 0 || !r.reading { + buf := make([]byte, len(b)) + copy(buf, b) + r.flows = append(r.flows, buf) + } else { + r.flows[l-1] = append(r.flows[l-1], b[:n]...) + } + r.reading = true + return +} + +func (r *recordingConn) Write(b []byte) (n int, err error) { + if n, err = r.Conn.Write(b); n == 0 { + return + } + b = b[:n] + + r.Lock() + defer r.Unlock() + + if l := len(r.flows); l == 0 || r.reading { + buf := make([]byte, len(b)) + copy(buf, b) + r.flows = append(r.flows, buf) + } else { + r.flows[l-1] = append(r.flows[l-1], b[:n]...) + } + r.reading = false + return +} + +// WriteTo writes hex dumps to w that contains the recorded traffic. +func (r *recordingConn) WriteTo(w io.Writer) { + // TLS always starts with a client to server flow. + clientToServer := true + + for i, flow := range r.flows { + source, dest := "client", "server" + if !clientToServer { + source, dest = dest, source + } + fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest) + dumper := hex.Dumper(w) + dumper.Write(flow) + dumper.Close() + clientToServer = !clientToServer + } +} + +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/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go new file mode 100644 index 0000000..137a87c --- /dev/null +++ b/src/ssl/test/runner/runner.go @@ -0,0 +1,2649 @@ +package main + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "flag" + "fmt" + "io" + "io/ioutil" + "net" + "os" + "os/exec" + "path" + "runtime" + "strconv" + "strings" + "sync" + "syscall" +) + +var ( + useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind") + useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb") + flagDebug *bool = flag.Bool("debug", false, "Hexdump the contents of the connection") + mallocTest *int64 = flag.Int64("malloc-test", -1, "If non-negative, run each test with each malloc in turn failing from the given number onwards.") + mallocTestDebug *bool = 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.") +) + +const ( + rsaCertificateFile = "cert.pem" + ecdsaCertificateFile = "ecdsa_cert.pem" +) + +const ( + rsaKeyFile = "key.pem" + ecdsaKeyFile = "ecdsa_key.pem" + channelIDKeyFile = "channel_id_key.pem" +) + +var rsaCertificate, ecdsaCertificate Certificate +var channelIDKey *ecdsa.PrivateKey +var channelIDBytes []byte + +var testOCSPResponse = []byte{1, 2, 3, 4} +var testSCTList = []byte{5, 6, 7, 8} + +func initCertificates() { + var err error + rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile) + if err != nil { + panic(err) + } + rsaCertificate.OCSPStaple = testOCSPResponse + rsaCertificate.SignedCertificateTimestampList = testSCTList + + ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile) + if err != nil { + panic(err) + } + ecdsaCertificate.OCSPStaple = testOCSPResponse + ecdsaCertificate.SignedCertificateTimestampList = testSCTList + + channelIDPEMBlock, err := ioutil.ReadFile(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) +} + +var certificateOnce sync.Once + +func getRSACertificate() Certificate { + certificateOnce.Do(initCertificates) + return rsaCertificate +} + +func getECDSACertificate() Certificate { + certificateOnce.Do(initCertificates) + return ecdsaCertificate +} + +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 + // 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 + // 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 + // messageLen is the length, in bytes, of the test message that will be + // sent. + messageLen 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 + // 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 + // 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 + // renegotiate indicates the the connection should be renegotiated + // during the exchange. + renegotiate 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 + // flags, if not empty, contains a list of command-line flags that will + // be passed to the shim program. + flags []string +} + +var testCases = []testCase{ + { + name: "BadRSASignature", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + InvalidSKXSignature: true, + }, + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + }, + { + name: "BadECDSASignature", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + InvalidSKXSignature: true, + }, + Certificates: []Certificate{getECDSACertificate()}, + }, + shouldFail: true, + expectedError: ":BAD_SIGNATURE:", + }, + { + name: "BadECDSACurve", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + InvalidSKXCurve: true, + }, + Certificates: []Certificate{getECDSACertificate()}, + }, + shouldFail: true, + expectedError: ":WRONG_CURVE:", + }, + { + testType: serverTest, + name: "BadRSAVersion", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + Bugs: ProtocolBugs{ + RsaClientKeyExchangeVersion: VersionTLS11, + }, + }, + shouldFail: true, + expectedError: ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:", + }, + { + 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{ + ClientAuth: RequestClientCert, + ClientCertificateTypes: []byte{ + CertTypeDSSSign, + CertTypeRSASign, + CertTypeECDSASign, + }, + }, + flags: []string{ + "-expect-certificate-types", + base64.StdEncoding.EncodeToString([]byte{ + CertTypeDSSSign, + CertTypeRSASign, + CertTypeECDSASign, + }), + }, + }, + { + name: "NoClientCertificate", + config: Config{ + ClientAuth: RequireAnyClientCert, + }, + shouldFail: true, + expectedLocalError: "client didn't provide a certificate", + }, + { + name: "UnauthenticatedECDH", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + UnauthenticatedECDH: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + name: "SkipServerKeyExchange", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SkipServerKeyExchange: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + name: "SkipChangeCipherSpec-Client", + config: Config{ + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "SkipChangeCipherSpec-Server", + config: Config{ + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "SkipChangeCipherSpec-Server-NPN", + config: Config{ + NextProtos: []string{"bar"}, + Bugs: ProtocolBugs{ + SkipChangeCipherSpec: true, + }, + }, + flags: []string{ + "-advertise-npn", "\x03foo\x03bar\x03baz", + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + name: "FragmentAcrossChangeCipherSpec-Client", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "FragmentAcrossChangeCipherSpec-Server", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + }, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "FragmentAcrossChangeCipherSpec-Server-NPN", + config: Config{ + NextProtos: []string{"bar"}, + Bugs: ProtocolBugs{ + FragmentAcrossChangeCipherSpec: true, + }, + }, + flags: []string{ + "-advertise-npn", "\x03foo\x03bar\x03baz", + }, + shouldFail: true, + expectedError: ":HANDSHAKE_RECORD_BEFORE_CCS:", + }, + { + testType: serverTest, + name: "FragmentAlert", + config: Config{ + Bugs: ProtocolBugs{ + FragmentAlert: true, + SendSpuriousAlert: true, + }, + }, + shouldFail: true, + expectedError: ":BAD_ALERT:", + }, + { + testType: serverTest, + name: "EarlyChangeCipherSpec-server-1", + config: Config{ + Bugs: ProtocolBugs{ + EarlyChangeCipherSpec: 1, + }, + }, + shouldFail: true, + expectedError: ":CCS_RECEIVED_EARLY:", + }, + { + testType: serverTest, + name: "EarlyChangeCipherSpec-server-2", + config: Config{ + Bugs: ProtocolBugs{ + EarlyChangeCipherSpec: 2, + }, + }, + shouldFail: true, + expectedError: ":CCS_RECEIVED_EARLY:", + }, + { + name: "SkipNewSessionTicket", + config: Config{ + Bugs: ProtocolBugs{ + SkipNewSessionTicket: true, + }, + }, + shouldFail: true, + expectedError: ":CCS_RECEIVED_EARLY:", + }, + { + testType: serverTest, + name: "FallbackSCSV", + config: Config{ + MaxVersion: VersionTLS11, + Bugs: ProtocolBugs{ + SendFallbackSCSV: true, + }, + }, + shouldFail: true, + expectedError: ":INAPPROPRIATE_FALLBACK:", + }, + { + testType: serverTest, + name: "FallbackSCSV-VersionMatch", + config: Config{ + Bugs: ProtocolBugs{ + SendFallbackSCSV: true, + }, + }, + }, + { + testType: serverTest, + name: "FragmentedClientVersion", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 1, + FragmentClientVersion: true, + }, + }, + expectedVersion: VersionTLS12, + }, + { + testType: serverTest, + name: "MinorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x03ff, + }, + }, + expectedVersion: VersionTLS12, + }, + { + testType: serverTest, + name: "MajorVersionTolerance", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0400, + }, + }, + expectedVersion: VersionTLS12, + }, + { + testType: serverTest, + name: "VersionTooLow", + config: Config{ + Bugs: ProtocolBugs{ + SendClientVersion: 0x0200, + }, + }, + shouldFail: true, + expectedError: ":UNSUPPORTED_PROTOCOL:", + }, + { + 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: ":UNKNOWN_PROTOCOL:", + }, + { + name: "SkipCipherVersionCheck", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + MaxVersion: VersionTLS11, + Bugs: ProtocolBugs{ + SkipCipherVersionCheck: true, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CIPHER_RETURNED:", + }, + { + name: "RSAServerKeyExchange", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + RSAServerKeyExchange: true, + }, + }, + shouldFail: true, + expectedError: ":UNEXPECTED_MESSAGE:", + }, + { + name: "DisableEverything", + flags: []string{"-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:", + }, + { + name: "NoSharedCipher", + config: Config{ + CipherSuites: []uint16{}, + }, + shouldFail: true, + expectedError: ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:", + }, + { + 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{ + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{getECDSACertificate()}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CERTIFICATE_TYPE:", + }, + { + name: "CertMismatchECDSA", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{getRSACertificate()}, + Bugs: ProtocolBugs{ + SendCipherSuite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + }, + }, + shouldFail: true, + expectedError: ":WRONG_CERTIFICATE_TYPE:", + }, + { + 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: "AppDataAfterChangeCipherSpec", + config: Config{ + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), + }, + }, + shouldFail: true, + expectedError: ":DATA_BETWEEN_CCS_AND_FINISHED:", + }, + { + protocol: dtls, + name: "AppDataAfterChangeCipherSpec-DTLS", + config: Config{ + Bugs: ProtocolBugs{ + AppDataAfterChangeCipherSpec: []byte("TEST MESSAGE"), + }, + }, + }, +} + +func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, isResume bool) error { + var connDebug *recordingConn + var connDamage *damageAdaptor + if *flagDebug { + connDebug = &recordingConn{Conn: conn} + conn = connDebug + defer func() { + connDebug.WriteTo(os.Stdout) + }() + } + + if test.protocol == dtls { + conn = newPacketAdaptor(conn) + if test.replayWrites { + conn = newReplayAdaptor(conn) + } + } + + 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) + } + } + + 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 + } + if vers := tlsConn.ConnectionState().Version; expectedVersion != 0 && vers != expectedVersion { + return fmt.Errorf("got version %x, expected %x", vers, expectedVersion) + } + + if test.expectChannelID { + channelID := tlsConn.ConnectionState().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 := tlsConn.ConnectionState().NegotiatedProtocol; actual != expected { + return fmt.Errorf("next proto mismatch: got %s, wanted %s", actual, expected) + } + } + + if test.expectedNextProtoType != 0 { + if (test.expectedNextProtoType == alpn) != tlsConn.ConnectionState().NegotiatedProtocolFromALPN { + return fmt.Errorf("next proto type mismatch") + } + } + + if p := tlsConn.ConnectionState().SRTPProtectionProfile; p != test.expectedSRTPProtectionProfile { + return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile) + } + + 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") + } + } + + if test.renegotiate { + if test.renegotiateCiphers != nil { + config.CipherSuites = test.renegotiateCiphers + } + 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) + } + + 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 + } + + var testMessage []byte + if config.Bugs.AppDataAfterChangeCipherSpec != nil { + // We've already sent a message. Expect the shim to echo it + // back. + testMessage = config.Bugs.AppDataAfterChangeCipherSpec + } else { + if messageLen == 0 { + messageLen = 32 + } + testMessage = make([]byte, messageLen) + for i := range testMessage { + testMessage[i] = 0x42 + } + tlsConn.Write(testMessage) + } + + 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"} + 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 openSocketPair() (shimEnd *os.File, conn net.Conn) { + socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0) + if err != nil { + panic(err) + } + + syscall.CloseOnExec(socks[0]) + syscall.CloseOnExec(socks[1]) + shimEnd = os.NewFile(uintptr(socks[0]), "shim end") + connFile := os.NewFile(uintptr(socks[1]), "our end") + conn, err = net.FileConn(connFile) + if err != nil { + panic(err) + } + connFile.Close() + if err != nil { + panic(err) + } + return shimEnd, conn +} + +type moreMallocsError struct{} + +func (moreMallocsError) Error() string { + return "child process did not exhaust all allocation calls" +} + +var errMoreMallocs = moreMallocsError{} + +func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { + if !test.shouldFail && (len(test.expectedError) > 0 || len(test.expectedLocalError) > 0) { + panic("Error expected without shouldFail in " + test.name) + } + + shimEnd, conn := openSocketPair() + shimEndResume, connResume := openSocketPair() + + shim_path := path.Join(buildDir, "ssl/test/bssl_shim") + var flags []string + if test.testType == serverTest { + flags = append(flags, "-server") + + flags = append(flags, "-key-file") + if test.keyFile == "" { + flags = append(flags, rsaKeyFile) + } else { + flags = append(flags, test.keyFile) + } + + flags = append(flags, "-cert-file") + if test.certFile == "" { + flags = append(flags, rsaCertificateFile) + } else { + flags = append(flags, test.certFile) + } + } + + if test.protocol == dtls { + flags = append(flags, "-dtls") + } + + if test.resumeSession { + flags = append(flags, "-resume") + } + + if test.shimWritesFirst { + flags = append(flags, "-shim-writes-first") + } + + flags = append(flags, test.flags...) + + var shim *exec.Cmd + if *useValgrind { + shim = valgrindOf(false, shim_path, flags...) + } else if *useGDB { + shim = gdbOf(shim_path, flags...) + } else { + shim = exec.Command(shim_path, flags...) + } + shim.ExtraFiles = []*os.File{shimEnd, shimEndResume} + shim.Stdin = os.Stdin + var stdoutBuf, stderrBuf bytes.Buffer + shim.Stdout = &stdoutBuf + shim.Stderr = &stderrBuf + if mallocNumToFail >= 0 { + shim.Env = []string{"MALLOC_NUMBER_TO_FAIL=" + strconv.FormatInt(mallocNumToFail, 10)} + if *mallocTestDebug { + shim.Env = append(shim.Env, "MALLOC_ABORT_ON_FAIL=1") + } + shim.Env = append(shim.Env, "_MALLOC_CHECK=1") + } + + if err := shim.Start(); err != nil { + panic(err) + } + shimEnd.Close() + shimEndResume.Close() + + config := test.config + config.ClientSessionCache = NewLRUClientSessionCache(1) + config.ServerSessionCache = NewLRUServerSessionCache(1) + if test.testType == clientTest { + if len(config.Certificates) == 0 { + config.Certificates = []Certificate{getRSACertificate()} + } + } + + err := doExchange(test, &config, conn, test.messageLen, + false /* not a resumption */) + conn.Close() + + if err == nil && test.resumeSession { + var resumeConfig Config + if test.resumeConfig != nil { + resumeConfig = *test.resumeConfig + if len(resumeConfig.Certificates) == 0 { + resumeConfig.Certificates = []Certificate{getRSACertificate()} + } + if !test.newSessionsOnResume { + resumeConfig.SessionTicketKey = config.SessionTicketKey + resumeConfig.ClientSessionCache = config.ClientSessionCache + resumeConfig.ServerSessionCache = config.ServerSessionCache + } + } else { + resumeConfig = config + } + err = doExchange(test, &resumeConfig, connResume, test.messageLen, + true /* resumption */) + } + connResume.Close() + + childErr := shim.Wait() + if exitError, ok := childErr.(*exec.ExitError); ok { + if exitError.Sys().(syscall.WaitStatus).ExitStatus() == 88 { + return errMoreMallocs + } + } + + stdout := string(stdoutBuf.Bytes()) + stderr := string(stderrBuf.Bytes()) + failed := err != nil || childErr != nil + correctFailure := len(test.expectedError) == 0 || strings.Contains(stdout, test.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 '" + test.expectedError + "' / '" + test.expectedLocalError + "')" + default: + panic("internal error") + } + + return fmt.Errorf("%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s", msg, localError, childError, string(stdoutBuf.Bytes()), stderr) + } + + if !*useValgrind && len(stderr) > 0 { + println(stderr) + } + + return nil +} + +var tlsVersions = []struct { + name string + version uint16 + flag string + hasDTLS bool +}{ + {"SSL3", VersionSSL30, "-no-ssl3", false}, + {"TLS1", VersionTLS10, "-no-tls1", true}, + {"TLS11", VersionTLS11, "-no-tls11", false}, + {"TLS12", VersionTLS12, "-no-tls12", true}, +} + +var testCipherSuites = []struct { + name string + id uint16 +}{ + {"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-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}, + {"ECDHE-PSK-WITH-AES-128-GCM-SHA256", TLS_ECDHE_PSK_WITH_AES_128_GCM_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-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA}, + {"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA}, + {"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA}, + {"PSK-RC4-SHA", TLS_PSK_WITH_RC4_128_SHA}, + {"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5}, + {"RC4-SHA", TLS_RSA_WITH_RC4_128_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") +} + +func isDTLSCipher(suiteName string) bool { + return !hasComponent(suiteName, "RC4") +} + +func addCipherSuiteTests() { + for _, suite := range testCipherSuites { + const psk = "12345" + const pskIdentity = "luggage combo" + + var cert Certificate + var certFile string + var keyFile string + if hasComponent(suite.name, "ECDSA") { + cert = getECDSACertificate() + certFile = ecdsaCertificateFile + keyFile = ecdsaKeyFile + } else { + cert = getRSACertificate() + certFile = rsaCertificateFile + keyFile = rsaKeyFile + } + + var flags []string + if hasComponent(suite.name, "PSK") { + flags = append(flags, + "-psk", psk, + "-psk-identity", pskIdentity) + } + + for _, ver := range tlsVersions { + if ver.version < VersionTLS12 && isTLS12Only(suite.name) { + continue + } + + testCases = append(testCases, testCase{ + testType: clientTest, + name: ver.name + "-" + suite.name + "-client", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + }, + flags: flags, + resumeSession: true, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: 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, + }, + certFile: certFile, + keyFile: keyFile, + flags: flags, + resumeSession: true, + }) + + if ver.hasDTLS && isDTLSCipher(suite.name) { + testCases = append(testCases, testCase{ + testType: clientTest, + protocol: dtls, + name: "D" + ver.name + "-" + suite.name + "-client", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + PreSharedKey: []byte(psk), + PreSharedKeyIdentity: pskIdentity, + }, + flags: flags, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + protocol: dtls, + name: "D" + 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, + }, + certFile: certFile, + keyFile: keyFile, + flags: flags, + resumeSession: true, + }) + } + } + } +} + +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{ + CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + Certificates: []Certificate{getECDSACertificate()}, + Bugs: ProtocolBugs{ + BadECDSAR: badR, + BadECDSAS: badS, + }, + }, + shouldFail: true, + expectedError: "SIGNATURE", + }) + } + } +} + +func addCBCPaddingTests() { + testCases = append(testCases, testCase{ + name: "MaxCBCPadding", + config: Config{ + 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{ + 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{ + 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 + 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. + // TODO(davidben): Add tests that those values parse out correctly. + certPool := x509.NewCertPool() + cert, err := x509.ParseCertificate(rsaCertificate.Certificate[0]) + if err != nil { + panic(err) + } + certPool.AddCert(cert) + + 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", rsaCertificateFile, + "-key-file", 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{ecdsaCertificate}, + }, + 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", ecdsaCertificateFile, + "-key-file", ecdsaKeyFile, + }, + }) + } + } +} + +func addExtendedMasterSecretTests() { + const expectEMSFlag = "-expect-extended-master-secret" + + for _, with := range []bool{false, true} { + prefix := "No" + var flags []string + if with { + prefix = "" + flags = []string{expectEMSFlag} + } + + for _, isClient := range []bool{false, true} { + suffix := "-Server" + testType := serverTest + if isClient { + suffix = "-Client" + testType = clientTest + } + + for _, ver := range tlsVersions { + 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) + } + } + } + + // 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. + testCases = append(testCases, testCase{ + name: "ExtendedMasterSecret-Resume", + config: Config{ + Bugs: ProtocolBugs{ + RequireExtendedMasterSecret: true, + }, + }, + flags: []string{expectEMSFlag}, + resumeSession: true, + }) +} + +// 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 addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) { + var suffix string + var flags []string + var maxHandshakeRecordLength int + if protocol == dtls { + suffix = "-DTLS" + } + if async { + suffix += "-Async" + flags = append(flags, "-async") + } else { + suffix += "-Sync" + } + if splitHandshake { + suffix += "-SplitHandshakeRecords" + maxHandshakeRecordLength = 1 + } + + // Basic handshake, with resumption. Client and server, + // session ID and session ticket. + testCases = append(testCases, testCase{ + protocol: protocol, + name: "Basic-Client" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: flags, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + name: "Basic-Client-RenewTicket" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + RenewTicketOnResume: true, + }, + }, + flags: flags, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + name: "Basic-Client-NoTicket" + suffix, + config: Config{ + SessionTicketsDisabled: true, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: flags, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "Basic-Server" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: flags, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "Basic-Server-NoTickets" + suffix, + config: Config{ + SessionTicketsDisabled: true, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: flags, + resumeSession: true, + }) + + // TLS client auth. + testCases = append(testCases, testCase{ + protocol: protocol, + testType: clientTest, + name: "ClientAuth-Client" + suffix, + config: Config{ + ClientAuth: RequireAnyClientCert, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, + "-cert-file", rsaCertificateFile, + "-key-file", rsaKeyFile), + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "ClientAuth-Server" + suffix, + config: Config{ + Certificates: []Certificate{rsaCertificate}, + }, + flags: append(flags, "-require-any-client-certificate"), + }) + + // No session ticket support; server doesn't send NewSessionTicket. + testCases = append(testCases, testCase{ + protocol: protocol, + name: "SessionTicketsDisabled-Client" + suffix, + config: Config{ + SessionTicketsDisabled: true, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: flags, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "SessionTicketsDisabled-Server" + suffix, + config: Config{ + SessionTicketsDisabled: true, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: flags, + }) + + // Skip ServerKeyExchange in PSK key exchange if there's no + // identity hint. + testCases = append(testCases, testCase{ + protocol: protocol, + name: "EmptyPSKHint-Client" + suffix, + config: Config{ + CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, + PreSharedKey: []byte("secret"), + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, "-psk", "secret"), + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "EmptyPSKHint-Server" + suffix, + config: Config{ + CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, + PreSharedKey: []byte("secret"), + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, "-psk", "secret"), + }) + + if protocol == tls { + // NPN on client and server; results in post-handshake message. + testCases = append(testCases, testCase{ + protocol: protocol, + name: "NPN-Client" + suffix, + config: Config{ + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, "-select-next-proto", "foo"), + expectedNextProto: "foo", + expectedNextProtoType: npn, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "NPN-Server" + suffix, + config: Config{ + NextProtos: []string{"bar"}, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, + "-advertise-npn", "\x03foo\x03bar\x03baz", + "-expect-next-proto", "bar"), + expectedNextProto: "bar", + expectedNextProtoType: npn, + }) + + // Client does False Start and negotiates NPN. + testCases = append(testCases, testCase{ + protocol: protocol, + name: "FalseStart" + suffix, + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, + "-false-start", + "-select-next-proto", "foo"), + shimWritesFirst: true, + resumeSession: true, + }) + + // Client does False Start and negotiates ALPN. + testCases = append(testCases, testCase{ + protocol: protocol, + name: "FalseStart-ALPN" + suffix, + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, + "-false-start", + "-advertise-alpn", "\x03foo"), + shimWritesFirst: true, + resumeSession: true, + }) + + // False Start without session tickets. + testCases = append(testCases, testCase{ + name: "FalseStart-SessionTicketsDisabled", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + NextProtos: []string{"foo"}, + SessionTicketsDisabled: true, + Bugs: ProtocolBugs{ + ExpectFalseStart: true, + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, + "-false-start", + "-select-next-proto", "foo", + ), + shimWritesFirst: true, + }) + + // Server parses a V2ClientHello. + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "SendV2ClientHello" + suffix, + config: Config{ + // Choose a cipher suite that does not involve + // elliptic curves, so no extensions are + // involved. + CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + SendV2ClientHello: true, + }, + }, + flags: flags, + }) + + // Client sends a Channel ID. + testCases = append(testCases, testCase{ + protocol: protocol, + name: "ChannelID-Client" + suffix, + config: Config{ + RequestChannelID: true, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, + "-send-channel-id", channelIDKeyFile, + ), + resumeSession: true, + expectChannelID: true, + }) + + // Server accepts a Channel ID. + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "ChannelID-Server" + suffix, + config: Config{ + ChannelID: channelIDKey, + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, + "-expect-channel-id", + base64.StdEncoding.EncodeToString(channelIDBytes), + ), + resumeSession: true, + expectChannelID: true, + }) + } else { + testCases = append(testCases, testCase{ + protocol: protocol, + name: "SkipHelloVerifyRequest" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + SkipHelloVerifyRequest: true, + }, + }, + flags: flags, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + protocol: protocol, + name: "CookieExchange" + suffix, + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: maxHandshakeRecordLength, + }, + }, + flags: append(flags, "-cookie-exchange"), + }) + } +} + +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) + } + + 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))) + + clientVers := shimVers.version + if clientVers > VersionTLS10 { + clientVers = VersionTLS10 + } + 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: expectedVersion, + }, + }, + flags: flags, + expectedVersion: expectedVersion, + }) + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "VersionNegotiation-Server2-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + Bugs: ProtocolBugs{ + ExpectInitialRecordVersion: expectedVersion, + }, + }, + flags: []string{"-max-version", shimVersFlag}, + expectedVersion: expectedVersion, + }) + } + } + } +} + +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 string + var expectedLocalError string + if runnerVers.version >= shimVers.version { + expectedVersion = runnerVers.version + } else { + shouldFail = true + expectedError = ":UNSUPPORTED_PROTOCOL:" + if runnerVers.version > VersionSSL30 { + expectedLocalError = "remote error: protocol version not supported" + } else { + expectedLocalError = "remote error: handshake failure" + } + } + + testCases = append(testCases, testCase{ + protocol: protocol, + testType: clientTest, + name: "MinimumVersion-Client-" + suffix, + config: Config{ + MaxVersion: runnerVers.version, + }, + 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, + }, + 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 addD5BugTests() { + testCases = append(testCases, testCase{ + testType: serverTest, + name: "D5Bug-NoQuirk-Reject", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SSL3RSAKeyExchange: true, + }, + }, + shouldFail: true, + expectedError: ":TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "D5Bug-Quirk-Normal", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + }, + flags: []string{"-tls-d5-bug"}, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "D5Bug-Quirk-Bug", + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + SSL3RSAKeyExchange: true, + }, + }, + flags: []string{"-tls-d5-bug"}, + }) +} + +func addExtensionTests() { + testCases = append(testCases, testCase{ + testType: clientTest, + name: "DuplicateExtensionClient", + config: Config{ + Bugs: ProtocolBugs{ + DuplicateExtension: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: error decoding message", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "DuplicateExtensionServer", + config: Config{ + Bugs: ProtocolBugs{ + DuplicateExtension: true, + }, + }, + shouldFail: true, + expectedLocalError: "remote error: error decoding message", + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ServerNameExtensionClient", + config: Config{ + Bugs: ProtocolBugs{ + ExpectServerName: "example.com", + }, + }, + flags: []string{"-host-name", "example.com"}, + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ServerNameExtensionClient", + config: Config{ + 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: "ServerNameExtensionClient", + config: Config{ + Bugs: ProtocolBugs{ + ExpectServerName: "missing.com", + }, + }, + shouldFail: true, + expectedLocalError: "tls: unexpected server name", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ServerNameExtensionServer", + config: Config{ + ServerName: "example.com", + }, + flags: []string{"-expect-server-name", "example.com"}, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: clientTest, + name: "ALPNClient", + config: Config{ + NextProtos: []string{"foo"}, + }, + flags: []string{ + "-advertise-alpn", "\x03foo\x03bar\x03baz", + "-expect-alpn", "foo", + }, + expectedNextProto: "foo", + expectedNextProtoType: alpn, + resumeSession: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer", + config: Config{ + NextProtos: []string{"foo", "bar", "baz"}, + }, + flags: []string{ + "-expect-advertised-alpn", "\x03foo\x03bar\x03baz", + "-select-alpn", "foo", + }, + expectedNextProto: "foo", + expectedNextProtoType: alpn, + resumeSession: true, + }) + // Test that the server prefers ALPN over NPN. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "ALPNServer-Preferred", + config: Config{ + 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", + config: Config{ + 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, + }) + // Resume with a corrupt ticket. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "CorruptTicket", + config: Config{ + Bugs: ProtocolBugs{ + CorruptTicket: true, + }, + }, + resumeSession: true, + flags: []string{"-expect-session-miss"}, + }) + // Resume with an oversized session id. + testCases = append(testCases, testCase{ + testType: serverTest, + name: "OversizedSessionId", + config: Config{ + Bugs: ProtocolBugs{ + OversizedSessionId: true, + }, + }, + resumeSession: true, + shouldFail: true, + expectedError: ":DECODE_ERROR:", + }) + // Basic DTLS-SRTP tests. Include fake profiles to ensure they + // are ignored. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "SRTP-Client", + config: Config{ + 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", + config: Config{ + 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", + config: Config{ + 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", + config: Config{ + 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", + config: Config{ + 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 OCSP stapling and SCT list. + testCases = append(testCases, testCase{ + name: "OCSPStapling", + flags: []string{ + "-enable-ocsp-stapling", + "-expect-ocsp-response", + base64.StdEncoding.EncodeToString(testOCSPResponse), + }, + }) + testCases = append(testCases, testCase{ + name: "SignedCertificateTimestampList", + flags: []string{ + "-enable-signed-cert-timestamps", + "-expect-signed-cert-timestamps", + base64.StdEncoding.EncodeToString(testSCTList), + }, + }) +} + +func addResumptionVersionTests() { + for _, sessionVers := range tlsVersions { + for _, resumeVers := range tlsVersions { + 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" + } + + testCases = append(testCases, testCase{ + protocol: protocol, + name: "Resume-Client" + suffix, + resumeSession: true, + config: Config{ + MaxVersion: sessionVers.version, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + AllowSessionVersionMismatch: true, + }, + }, + expectedVersion: sessionVers.version, + resumeConfig: &Config{ + MaxVersion: resumeVers.version, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + Bugs: ProtocolBugs{ + AllowSessionVersionMismatch: true, + }, + }, + expectedResumeVersion: resumeVers.version, + }) + + testCases = append(testCases, testCase{ + protocol: protocol, + name: "Resume-Client-NoResume" + suffix, + flags: []string{"-expect-session-miss"}, + resumeSession: true, + config: Config{ + MaxVersion: sessionVers.version, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + }, + expectedVersion: sessionVers.version, + resumeConfig: &Config{ + MaxVersion: resumeVers.version, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + }, + newSessionsOnResume: true, + expectedResumeVersion: resumeVers.version, + }) + + var flags []string + if sessionVers.version != resumeVers.version { + flags = append(flags, "-expect-session-miss") + } + testCases = append(testCases, testCase{ + protocol: protocol, + testType: serverTest, + name: "Resume-Server" + suffix, + flags: flags, + resumeSession: true, + config: Config{ + MaxVersion: sessionVers.version, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + }, + expectedVersion: sessionVers.version, + resumeConfig: &Config{ + MaxVersion: resumeVers.version, + CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + }, + expectedResumeVersion: resumeVers.version, + }) + } + } + } +} + +func addRenegotiationTests() { + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server", + flags: []string{"-renegotiate"}, + shimWritesFirst: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-EmptyExt", + config: Config{ + Bugs: ProtocolBugs{ + EmptyRenegotiationInfo: true, + }, + }, + flags: []string{"-renegotiate"}, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-BadExt", + config: Config{ + Bugs: ProtocolBugs{ + BadRenegotiationInfo: true, + }, + }, + flags: []string{"-renegotiate"}, + shimWritesFirst: true, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-ClientInitiated", + renegotiate: true, + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-ClientInitiated-NoExt", + renegotiate: true, + config: Config{ + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + }, + }, + shouldFail: true, + expectedError: ":UNSAFE_LEGACY_RENEGOTIATION_DISABLED:", + }) + testCases = append(testCases, testCase{ + testType: serverTest, + name: "Renegotiate-Server-ClientInitiated-NoExt-Allowed", + renegotiate: true, + config: Config{ + Bugs: ProtocolBugs{ + NoRenegotiationInfo: true, + }, + }, + flags: []string{"-allow-unsafe-legacy-renegotiation"}, + }) + // TODO(agl): test the renegotiation info SCSV. + testCases = append(testCases, testCase{ + name: "Renegotiate-Client", + renegotiate: true, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-EmptyExt", + renegotiate: true, + config: Config{ + Bugs: ProtocolBugs{ + EmptyRenegotiationInfo: true, + }, + }, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-BadExt", + renegotiate: true, + config: Config{ + Bugs: ProtocolBugs{ + BadRenegotiationInfo: true, + }, + }, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-SwitchCiphers", + renegotiate: true, + config: Config{ + CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + }, + renegotiateCiphers: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-Client-SwitchCiphers2", + renegotiate: true, + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + }, + renegotiateCiphers: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + }) + testCases = append(testCases, testCase{ + name: "Renegotiate-SameClientVersion", + renegotiate: true, + config: Config{ + MaxVersion: VersionTLS10, + Bugs: ProtocolBugs{ + RequireSameRenegoClientVersion: true, + }, + }, + }) +} + +func addDTLSReplayTests() { + // Test that sequence number replays are detected. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Replay", + replayWrites: true, + }) + + // Test the outgoing sequence number skipping by values larger + // than the retransmit window. + testCases = append(testCases, testCase{ + protocol: dtls, + name: "DTLS-Replay-LargeGaps", + config: Config{ + Bugs: ProtocolBugs{ + SequenceNumberIncrement: 127, + }, + }, + replayWrites: true, + }) +} + +func addFastRadioPaddingTests() { + testCases = append(testCases, testCase{ + protocol: tls, + name: "FastRadio-Padding", + config: Config{ + Bugs: ProtocolBugs{ + RequireFastradioPadding: true, + }, + }, + flags: []string{"-fastradio-padding"}, + }) + testCases = append(testCases, testCase{ + protocol: dtls, + name: "FastRadio-Padding", + config: Config{ + Bugs: ProtocolBugs{ + RequireFastradioPadding: true, + }, + }, + flags: []string{"-fastradio-padding"}, + }) +} + +var testHashes = []struct { + name string + id uint8 +}{ + {"SHA1", hashSHA1}, + {"SHA224", hashSHA224}, + {"SHA256", hashSHA256}, + {"SHA384", hashSHA384}, + {"SHA512", hashSHA512}, +} + +func addSigningHashTests() { + // Make sure each hash works. Include some fake hashes in the list and + // ensure they're ignored. + for _, hash := range testHashes { + testCases = append(testCases, testCase{ + name: "SigningHash-ClientAuth-" + hash.name, + config: Config{ + ClientAuth: RequireAnyClientCert, + SignatureAndHashes: []signatureAndHash{ + {signatureRSA, 42}, + {signatureRSA, hash.id}, + {signatureRSA, 255}, + }, + }, + flags: []string{ + "-cert-file", rsaCertificateFile, + "-key-file", rsaKeyFile, + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SigningHash-ServerKeyExchange-Sign-" + hash.name, + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + SignatureAndHashes: []signatureAndHash{ + {signatureRSA, 42}, + {signatureRSA, hash.id}, + {signatureRSA, 255}, + }, + }, + }) + } + + // Test that hash resolution takes the signature type into account. + testCases = append(testCases, testCase{ + name: "SigningHash-ClientAuth-SignatureType", + config: Config{ + ClientAuth: RequireAnyClientCert, + SignatureAndHashes: []signatureAndHash{ + {signatureECDSA, hashSHA512}, + {signatureRSA, hashSHA384}, + {signatureECDSA, hashSHA1}, + }, + }, + flags: []string{ + "-cert-file", rsaCertificateFile, + "-key-file", rsaKeyFile, + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SigningHash-ServerKeyExchange-SignatureType", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + SignatureAndHashes: []signatureAndHash{ + {signatureECDSA, hashSHA512}, + {signatureRSA, hashSHA384}, + {signatureECDSA, hashSHA1}, + }, + }, + }) + + // Test that, if the list is missing, the peer falls back to SHA-1. + testCases = append(testCases, testCase{ + name: "SigningHash-ClientAuth-Fallback", + config: Config{ + ClientAuth: RequireAnyClientCert, + SignatureAndHashes: []signatureAndHash{ + {signatureRSA, hashSHA1}, + }, + Bugs: ProtocolBugs{ + NoSignatureAndHashes: true, + }, + }, + flags: []string{ + "-cert-file", rsaCertificateFile, + "-key-file", rsaKeyFile, + }, + }) + + testCases = append(testCases, testCase{ + testType: serverTest, + name: "SigningHash-ServerKeyExchange-Fallback", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, + SignatureAndHashes: []signatureAndHash{ + {signatureRSA, hashSHA1}, + }, + Bugs: ProtocolBugs{ + NoSignatureAndHashes: true, + }, + }, + }) +} + +func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) { + defer wg.Done() + + for test := range c { + var err error + + if *mallocTest < 0 { + statusChan <- statusMsg{test: test, started: true} + err = runTest(test, buildDir, -1) + } else { + for mallocNumToFail := int64(*mallocTest); ; mallocNumToFail++ { + statusChan <- statusMsg{test: test, started: true} + if err = runTest(test, buildDir, mallocNumToFail); err != errMoreMallocs { + if err != nil { + fmt.Printf("\n\nmalloc test failed at %d: %s\n", mallocNumToFail, err) + } + break + } + } + } + statusChan <- statusMsg{test: test, err: err} + } +} + +type statusMsg struct { + test *testCase + started bool + err error +} + +func statusPrinter(doneChan chan struct{}, statusChan chan statusMsg, total int) { + var started, done, failed, lineLen int + defer close(doneChan) + + for msg := range statusChan { + if msg.started { + started++ + } else { + done++ + } + + fmt.Printf("\x1b[%dD\x1b[K", lineLen) + + if msg.err != nil { + fmt.Printf("FAILED (%s)\n%s\n", msg.test.name, msg.err) + failed++ + } + line := fmt.Sprintf("%d/%d/%d/%d", failed, done, started, total) + lineLen = len(line) + os.Stdout.WriteString(line) + } +} + +func main() { + var flagTest *string = flag.String("test", "", "The name of a test to run, or empty to run all tests") + var flagNumWorkers *int = flag.Int("num-workers", runtime.NumCPU(), "The number of workers to run in parallel.") + var flagBuildDir *string = flag.String("build-dir", "../../../build", "The build directory to run the shim from.") + + flag.Parse() + + addCipherSuiteTests() + addBadECDSASignatureTests() + addCBCPaddingTests() + addCBCSplittingTests() + addClientAuthTests() + addVersionNegotiationTests() + addMinimumVersionTests() + addD5BugTests() + addExtensionTests() + addResumptionVersionTests() + addExtendedMasterSecretTests() + addRenegotiationTests() + addDTLSReplayTests() + addSigningHashTests() + addFastRadioPaddingTests() + for _, async := range []bool{false, true} { + for _, splitHandshake := range []bool{false, true} { + for _, protocol := range []protocol{tls, dtls} { + addStateMachineCoverageTests(async, splitHandshake, protocol) + } + } + } + + var wg sync.WaitGroup + + numWorkers := *flagNumWorkers + + statusChan := make(chan statusMsg, numWorkers) + testChan := make(chan *testCase, numWorkers) + doneChan := make(chan struct{}) + + go statusPrinter(doneChan, statusChan, len(testCases)) + + for i := 0; i < numWorkers; i++ { + wg.Add(1) + go worker(statusChan, testChan, *flagBuildDir, &wg) + } + + for i := range testCases { + if len(*flagTest) == 0 || *flagTest == testCases[i].name { + testChan <- &testCases[i] + } + } + + close(testChan) + wg.Wait() + close(statusChan) + <-doneChan + + fmt.Printf("\n") +} diff --git a/src/ssl/test/runner/ticket.go b/src/ssl/test/runner/ticket.go new file mode 100644 index 0000000..8355822 --- /dev/null +++ b/src/ssl/test/runner/ticket.go @@ -0,0 +1,221 @@ +// 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 main + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/sha256" + "crypto/subtle" + "errors" + "io" +) + +// 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 +} + +func (s *sessionState) equal(i interface{}) bool { + s1, ok := i.(*sessionState) + if !ok { + return false + } + + if s.vers != s1.vers || + s.cipherSuite != s1.cipherSuite || + !bytes.Equal(s.masterSecret, s1.masterSecret) || + !bytes.Equal(s.handshakeHash, s1.handshakeHash) || + s.extendedMasterSecret != s1.extendedMasterSecret { + return false + } + + if len(s.certificates) != len(s1.certificates) { + return false + } + + for i := range s.certificates { + if !bytes.Equal(s.certificates[i], s1.certificates[i]) { + return false + } + } + + return true +} + +func (s *sessionState) marshal() []byte { + length := 2 + 2 + 2 + len(s.masterSecret) + 2 + len(s.handshakeHash) + 2 + for _, cert := range s.certificates { + length += 4 + len(cert) + } + length++ + + ret := make([]byte, length) + x := ret + x[0] = byte(s.vers >> 8) + x[1] = byte(s.vers) + x[2] = byte(s.cipherSuite >> 8) + x[3] = byte(s.cipherSuite) + x[4] = byte(len(s.masterSecret) >> 8) + x[5] = byte(len(s.masterSecret)) + x = x[6:] + copy(x, s.masterSecret) + x = x[len(s.masterSecret):] + + x[0] = byte(len(s.handshakeHash) >> 8) + x[1] = byte(len(s.handshakeHash)) + x = x[2:] + copy(x, s.handshakeHash) + x = x[len(s.handshakeHash):] + + x[0] = byte(len(s.certificates) >> 8) + x[1] = byte(len(s.certificates)) + x = x[2:] + + for _, cert := range s.certificates { + x[0] = byte(len(cert) >> 24) + x[1] = byte(len(cert) >> 16) + x[2] = byte(len(cert) >> 8) + x[3] = byte(len(cert)) + copy(x[4:], cert) + x = x[4+len(cert):] + } + + if s.extendedMasterSecret { + x[0] = 1 + } + x = x[1:] + + return ret +} + +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 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/src/ssl/test/runner/tls.go b/src/ssl/test/runner/tls.go new file mode 100644 index 0000000..6b637c8 --- /dev/null +++ b/src/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 main + +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/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc new file mode 100644 index 0000000..c032d96 --- /dev/null +++ b/src/ssl/test/test_config.cc @@ -0,0 +1,198 @@ +/* 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 }, + { "-resume", &TestConfig::resume }, + { "-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-tls12", &TestConfig::no_tls12 }, + { "-no-tls11", &TestConfig::no_tls11 }, + { "-no-tls1", &TestConfig::no_tls1 }, + { "-no-ssl3", &TestConfig::no_ssl3 }, + { "-cookie-exchange", &TestConfig::cookie_exchange }, + { "-shim-writes-first", &TestConfig::shim_writes_first }, + { "-tls-d5-bug", &TestConfig::tls_d5_bug }, + { "-expect-session-miss", &TestConfig::expect_session_miss }, + { "-expect-extended-master-secret", + &TestConfig::expect_extended_master_secret }, + { "-renegotiate", &TestConfig::renegotiate }, + { "-allow-unsafe-legacy-renegotiation", + &TestConfig::allow_unsafe_legacy_renegotiation }, + { "-enable-ocsp-stapling", &TestConfig::enable_ocsp_stapling }, + { "-enable-signed-cert-timestamps", + &TestConfig::enable_signed_cert_timestamps }, + { "-fastradio-padding", &TestConfig::fastradio_padding }, +}; + +const Flag<std::string> kStringFlags[] = { + { "-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 }, +}; + +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 }, +}; + +const Flag<int> kIntFlags[] = { + { "-min-version", &TestConfig::min_version }, + { "-max-version", &TestConfig::max_version }, + { "-mtu", &TestConfig::mtu }, +}; + +} // namespace + +TestConfig::TestConfig() + : is_server(false), + is_dtls(false), + resume(false), + fallback_scsv(false), + require_any_client_certificate(false), + false_start(false), + async(false), + write_different_record_sizes(false), + cbc_record_splitting(false), + partial_write(false), + no_tls12(false), + no_tls11(false), + no_tls1(false), + no_ssl3(false), + cookie_exchange(false), + shim_writes_first(false), + tls_d5_bug(false), + expect_session_miss(false), + expect_extended_master_secret(false), + renegotiate(false), + allow_unsafe_legacy_renegotiation(false), + enable_ocsp_stapling(false), + enable_signed_cert_timestamps(false), + fastradio_padding(false), + min_version(0), + max_version(0), + mtu(0) { +} + +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]); + } + 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]); + } + 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; + } + + fprintf(stderr, "Unknown argument: %s\n", argv[i]); + return false; + } + + return true; +} diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h new file mode 100644 index 0000000..ba54227 --- /dev/null +++ b/src/ssl/test/test_config.h @@ -0,0 +1,75 @@ +/* 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> + + +struct TestConfig { + TestConfig(); + + bool is_server; + bool is_dtls; + bool resume; + bool fallback_scsv; + std::string key_file; + std::string cert_file; + std::string expected_server_name; + std::string expected_certificate_types; + bool require_any_client_certificate; + std::string advertise_npn; + std::string expected_next_proto; + bool false_start; + std::string select_next_proto; + bool async; + bool write_different_record_sizes; + bool cbc_record_splitting; + bool partial_write; + bool no_tls12; + bool no_tls11; + bool no_tls1; + bool no_ssl3; + bool cookie_exchange; + std::string expected_channel_id; + std::string send_channel_id; + bool shim_writes_first; + bool tls_d5_bug; + std::string host_name; + std::string advertise_alpn; + std::string expected_alpn; + std::string expected_advertised_alpn; + std::string select_alpn; + bool expect_session_miss; + bool expect_extended_master_secret; + std::string psk; + std::string psk_identity; + bool renegotiate; + bool allow_unsafe_legacy_renegotiation; + std::string srtp_profiles; + bool enable_ocsp_stapling; + std::string expected_ocsp_response; + bool enable_signed_cert_timestamps; + std::string expected_signed_cert_timestamps; + bool fastradio_padding; + int min_version; + int max_version; + int mtu; +}; + +bool ParseConfig(int argc, char **argv, TestConfig *out_config); + + +#endif // HEADER_TEST_CONFIG |