summaryrefslogtreecommitdiffstats
path: root/src/ssl
diff options
context:
space:
mode:
authorAdam Langley <agl@google.com>2015-01-22 14:27:53 -0800
committerAdam Langley <agl@google.com>2015-01-30 16:52:14 -0800
commitd9e397b599b13d642138480a28c14db7a136bf05 (patch)
tree34bab61dc4ce323b123ad4614dbc07e86ea2f9ef /src/ssl
downloadexternal_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.zip
external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.gz
external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.bz2
Initial commit of BoringSSL for Android.
Diffstat (limited to 'src/ssl')
-rw-r--r--src/ssl/CMakeLists.txt45
-rw-r--r--src/ssl/d1_both.c1173
-rw-r--r--src/ssl/d1_clnt.c577
-rw-r--r--src/ssl/d1_lib.c439
-rw-r--r--src/ssl/d1_meth.c136
-rw-r--r--src/ssl/d1_pkt.c1321
-rw-r--r--src/ssl/d1_srtp.c431
-rw-r--r--src/ssl/d1_srvr.c605
-rw-r--r--src/ssl/pqueue/CMakeLists.txt17
-rw-r--r--src/ssl/pqueue/pqueue.c194
-rw-r--r--src/ssl/pqueue/pqueue_test.c117
-rw-r--r--src/ssl/s3_both.c699
-rw-r--r--src/ssl/s3_clnt.c2417
-rw-r--r--src/ssl/s3_enc.c527
-rw-r--r--src/ssl/s3_lib.c1548
-rw-r--r--src/ssl/s3_meth.c171
-rw-r--r--src/ssl/s3_pkt.c1216
-rw-r--r--src/ssl/s3_srvr.c2807
-rw-r--r--src/ssl/ssl_algs.c71
-rw-r--r--src/ssl/ssl_asn1.c591
-rw-r--r--src/ssl/ssl_cert.c1081
-rw-r--r--src/ssl/ssl_ciph.c1421
-rw-r--r--src/ssl/ssl_error.c566
-rw-r--r--src/ssl/ssl_lib.c3174
-rw-r--r--src/ssl/ssl_locl.h1035
-rw-r--r--src/ssl/ssl_rsa.c707
-rw-r--r--src/ssl/ssl_sess.c920
-rw-r--r--src/ssl/ssl_stat.c1018
-rw-r--r--src/ssl/ssl_test.c456
-rw-r--r--src/ssl/ssl_txt.c211
-rw-r--r--src/ssl/t1_enc.c1042
-rw-r--r--src/ssl/t1_lib.c2671
-rw-r--r--src/ssl/t1_reneg.c247
-rw-r--r--src/ssl/test/CMakeLists.txt16
-rw-r--r--src/ssl/test/async_bio.cc180
-rw-r--r--src/ssl/test/async_bio.h40
-rw-r--r--src/ssl/test/bssl_shim.cc758
-rw-r--r--src/ssl/test/malloc.cc130
-rw-r--r--src/ssl/test/packeted_bio.cc135
-rw-r--r--src/ssl/test/packeted_bio.h32
-rw-r--r--src/ssl/test/runner/alert.go77
-rw-r--r--src/ssl/test/runner/cert.pem15
-rw-r--r--src/ssl/test/runner/channel_id_key.pem5
-rw-r--r--src/ssl/test/runner/cipher_suites.go395
-rw-r--r--src/ssl/test/runner/common.go953
-rw-r--r--src/ssl/test/runner/conn.go1229
-rw-r--r--src/ssl/test/runner/dtls.go342
-rw-r--r--src/ssl/test/runner/ecdsa_cert.pem12
-rw-r--r--src/ssl/test/runner/ecdsa_key.pem8
-rw-r--r--src/ssl/test/runner/handshake_client.go910
-rw-r--r--src/ssl/test/runner/handshake_messages.go1875
-rw-r--r--src/ssl/test/runner/handshake_server.go964
-rw-r--r--src/ssl/test/runner/key.pem15
-rw-r--r--src/ssl/test/runner/key_agreement.go776
-rw-r--r--src/ssl/test/runner/packet_adapter.go101
-rw-r--r--src/ssl/test/runner/prf.go388
-rw-r--r--src/ssl/test/runner/recordingconn.go130
-rw-r--r--src/ssl/test/runner/runner.go2649
-rw-r--r--src/ssl/test/runner/ticket.go221
-rw-r--r--src/ssl/test/runner/tls.go279
-rw-r--r--src/ssl/test/test_config.cc198
-rw-r--r--src/ssl/test/test_config.h75
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(&parameter, 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(&parameter),
+ CBS_len(&parameter)) ||
+ !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, &copy);
+ 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(&copy) != 0) {
+ CBS proto;
+ if (!CBS_get_u8_length_prefixed(&copy, &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