external/boringssl: Sync to 2070f8ad9151dc8f3a73bffaa146b5e6937a583f.
This includes the following changes:
https://boringssl.googlesource.com/boringssl/+log/8ebeabf0e2e01b331e56d0a491c12539baa55d3d..2070f8ad9151dc8f3a73bffaa146b5e6937a583f
Test: BoringSSL CTS Presubmits
Change-Id: Ia779c6476e45c44e426e09afeca65b2192e783ae
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index b864e42..44e3f2e 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -123,7 +123,6 @@
#include <openssl/mem.h>
#include <openssl/rand.h>
#include <openssl/type_check.h>
-#include <openssl/x509.h>
#include "../crypto/internal.h"
#include "internal.h"
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index c457772..1feb7d8 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -164,8 +164,6 @@
#include <openssl/md5.h>
#include <openssl/mem.h>
#include <openssl/rand.h>
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
#include "../crypto/internal.h"
#include "internal.h"
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index e3a4e51..81e45ef 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -930,6 +930,9 @@
case ssl_session_retry:
ssl->rwstate = SSL_PENDING_SESSION;
goto err;
+ case ssl_session_ticket_retry:
+ ssl->rwstate = SSL_PENDING_TICKET;
+ goto err;
}
if (session != NULL) {
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index caa9cd2..99980d8 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -226,14 +226,14 @@
/* ssl_create_cipher_list evaluates |rule_str| according to the ciphers in
* |ssl_method|. It sets |*out_cipher_list| to a newly-allocated
- * |ssl_cipher_preference_list_st| containing the result. It returns
- * |(*out_cipher_list)->ciphers| on success and NULL on failure. If |strict| is
- * true, nonsense will be rejected. If false, nonsense will be silently
- * ignored. */
-STACK_OF(SSL_CIPHER) *
-ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method,
- struct ssl_cipher_preference_list_st **out_cipher_list,
- const char *rule_str, int strict);
+ * |ssl_cipher_preference_list_st| containing the result. It returns 1 on
+ * success and 0 on failure. If |strict| is true, nonsense will be rejected. If
+ * false, nonsense will be silently ignored. An empty result is considered an
+ * error regardless of |strict|. */
+int ssl_create_cipher_list(
+ const SSL_PROTOCOL_METHOD *ssl_method,
+ struct ssl_cipher_preference_list_st **out_cipher_list,
+ const char *rule_str, int strict);
/* ssl_cipher_get_value returns the cipher suite id of |cipher|. */
uint16_t ssl_cipher_get_value(const SSL_CIPHER *cipher);
@@ -352,6 +352,8 @@
* records. */
uint8_t fixed_nonce[12];
uint8_t fixed_nonce_len, variable_nonce_len;
+ /* version is the protocol version that should be used with this AEAD. */
+ uint16_t version;
/* variable_nonce_included_in_record is non-zero if the variable nonce
* for a record is included as a prefix before the ciphertext. */
unsigned variable_nonce_included_in_record : 1;
@@ -927,6 +929,7 @@
ssl_hs_x509_lookup,
ssl_hs_channel_id_lookup,
ssl_hs_private_key_operation,
+ ssl_hs_pending_ticket,
};
struct ssl_handshake_st {
@@ -1158,10 +1161,9 @@
int ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs,
uint8_t *out_alert, CBS *contents);
-int ssl_ext_pre_shared_key_parse_clienthello(SSL_HANDSHAKE *hs,
- SSL_SESSION **out_session,
- CBS *out_binders,
- uint8_t *out_alert, CBS *contents);
+int ssl_ext_pre_shared_key_parse_clienthello(
+ SSL_HANDSHAKE *hs, CBS *out_ticket, CBS *out_binders,
+ uint32_t *out_obfuscated_ticket_age, uint8_t *out_alert, CBS *contents);
int ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
/* ssl_is_sct_list_valid does a shallow parse of the SCT list in |contents| and
@@ -1679,6 +1681,11 @@
* verified Channel ID from the client: a P256 point, (x,y), where
* each are big-endian values. */
uint8_t tlsext_channel_id[64];
+
+ /* ticket_age_skew is the difference, in seconds, between the client-sent
+ * ticket age and the server-computed value in TLS 1.3 server connections
+ * which resumed a session. */
+ int32_t ticket_age_skew;
} SSL3_STATE;
/* lengths of messages */
@@ -1875,7 +1882,9 @@
size_t supported_group_list_len;
uint16_t *supported_group_list; /* our list */
- SSL_CTX *initial_ctx; /* initial ctx, used to store sessions */
+ /* session_ctx is the |SSL_CTX| used for the session cache and related
+ * settings. */
+ SSL_CTX *session_ctx;
/* srtp_profiles is the list of configured SRTP protection profiles for
* DTLS-SRTP. */
@@ -1983,13 +1992,15 @@
ssl_session_success,
ssl_session_error,
ssl_session_retry,
+ ssl_session_ticket_retry,
};
/* ssl_get_prev_session looks up the previous session based on |client_hello|.
* On success, it sets |*out_session| to the session or NULL if none was found.
* If the session could not be looked up synchronously, it returns
- * |ssl_session_retry| and should be called again. Otherwise, it returns
- * |ssl_session_error|. */
+ * |ssl_session_retry| and should be called again. If a ticket could not be
+ * decrypted immediately it returns |ssl_session_ticket_retry| and should also
+ * be called again. Otherwise, it returns |ssl_session_error|. */
enum ssl_session_result_t ssl_get_prev_session(
SSL *ssl, SSL_SESSION **out_session, int *out_tickets_supported,
int *out_renew_ticket, const SSL_CLIENT_HELLO *client_hello);
@@ -2027,8 +2038,6 @@
void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
-int ssl_verify_alarm_type(long type);
-
int ssl3_get_finished(SSL_HANDSHAKE *hs);
int ssl3_send_alert(SSL *ssl, int level, int desc);
int ssl3_get_message(SSL *ssl);
@@ -2159,15 +2168,19 @@
#define tlsext_tick_md EVP_sha256
-/* tls_process_ticket processes a session ticket from the client. On success,
- * it sets |*out_session| to the decrypted session or NULL if the ticket was
- * rejected. If the ticket was valid, it sets |*out_renew_ticket| to whether
- * the ticket should be renewed. It returns one on success and zero on fatal
- * error. */
-int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
- int *out_renew_ticket, const uint8_t *ticket,
- size_t ticket_len, const uint8_t *session_id,
- size_t session_id_len);
+/* ssl_process_ticket processes a session ticket from the client. It returns
+ * one of:
+ * |ssl_ticket_aead_success|: |*out_session| is set to the parsed session and
+ * |*out_renew_ticket| is set to whether the ticket should be renewed.
+ * |ssl_ticket_aead_ignore_ticket|: |*out_renew_ticket| is set to whether a
+ * fresh ticket should be sent, but the given ticket cannot be used.
+ * |ssl_ticket_aead_retry|: the ticket could not be immediately decrypted.
+ * Retry later.
+ * |ssl_ticket_aead_error|: an error occured that is fatal to the connection. */
+enum ssl_ticket_aead_result_t ssl_process_ticket(
+ SSL *ssl, SSL_SESSION **out_session, int *out_renew_ticket,
+ const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id,
+ size_t session_id_len);
/* tls1_verify_channel_id processes the current message as a Channel ID message,
* and verifies the signature. If the key is valid, it saves the Channel ID and
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 6b03030..8fa51e9 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -125,7 +125,6 @@
#include <openssl/nid.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
-#include <openssl/x509.h>
#include "../crypto/internal.h"
#include "internal.h"
@@ -770,80 +769,6 @@
}
}
-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:
- case X509_V_ERR_HOSTNAME_MISMATCH:
- case X509_V_ERR_EMAIL_MISMATCH:
- case X509_V_ERR_IP_ADDRESS_MISMATCH:
- 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_UNSPECIFIED:
- case X509_V_ERR_OUT_OF_MEM:
- case X509_V_ERR_INVALID_CALL:
- case X509_V_ERR_STORE_LOOKUP:
- 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 ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
const SSL_EXTENSION_TYPE *ext_types,
size_t num_ext_types, int ignore_unknown) {
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c
index e18ba69..1af4a5a 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.c
@@ -66,6 +66,7 @@
}
OPENSSL_memset(aead_ctx, 0, sizeof(SSL_AEAD_CTX));
aead_ctx->cipher = cipher;
+ aead_ctx->version = version;
if (!EVP_AEAD_CTX_init_with_direction(
&aead_ctx->ctx, aead, enc_key, enc_key_len,
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 1309a18..5013b20 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -127,7 +127,6 @@
#include <openssl/mem.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
-#include <openssl/x509v3.h>
#include "../crypto/internal.h"
#include "internal.h"
@@ -249,18 +248,33 @@
c->cert_cb_arg = arg;
}
-int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
+enum leaf_cert_and_privkey_result_t {
+ leaf_cert_and_privkey_error,
+ leaf_cert_and_privkey_ok,
+ leaf_cert_and_privkey_mismatch,
+};
+
+/* check_leaf_cert_and_privkey checks whether the certificate in |leaf_buffer|
+ * and the private key in |privkey| are suitable and coherent. It returns
+ * |leaf_cert_and_privkey_error| and pushes to the error queue if a problem is
+ * found. If the certificate and private key are valid, but incoherent, it
+ * returns |leaf_cert_and_privkey_mismatch|. Otherwise it returns
+ * |leaf_cert_and_privkey_ok|. */
+static enum leaf_cert_and_privkey_result_t check_leaf_cert_and_privkey(
+ CRYPTO_BUFFER *leaf_buffer, EVP_PKEY *privkey) {
+ enum leaf_cert_and_privkey_result_t ret = leaf_cert_and_privkey_error;
+
CBS cert_cbs;
- CRYPTO_BUFFER_init_CBS(buffer, &cert_cbs);
+ CRYPTO_BUFFER_init_CBS(leaf_buffer, &cert_cbs);
EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
if (pubkey == NULL) {
- return 0;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ goto out;
}
if (!ssl_is_key_type_supported(pubkey->type)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
- EVP_PKEY_free(pubkey);
- return 0;
+ goto out;
}
/* An ECC certificate may be usable for ECDH or ECDSA. We only support ECDSA
@@ -268,26 +282,102 @@
if (pubkey->type == EVP_PKEY_EC &&
!ssl_cert_check_digital_signature_key_usage(&cert_cbs)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
- EVP_PKEY_free(pubkey);
+ goto out;
+ }
+
+ if (privkey != NULL &&
+ /* Sanity-check that the private key and the certificate match. */
+ !ssl_compare_public_and_private_key(pubkey, privkey)) {
+ ERR_clear_error();
+ ret = leaf_cert_and_privkey_mismatch;
+ goto out;
+ }
+
+ ret = leaf_cert_and_privkey_ok;
+
+out:
+ EVP_PKEY_free(pubkey);
+ return ret;
+}
+
+static int cert_set_chain_and_key(
+ CERT *cert, CRYPTO_BUFFER *const *certs, size_t num_certs,
+ EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method) {
+ if (num_certs == 0 ||
+ (privkey == NULL && privkey_method == NULL)) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
- if (cert->privatekey != NULL) {
- /* 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(cert->privatekey) &&
- !ssl_compare_public_and_private_key(pubkey, cert->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(cert->privatekey);
- cert->privatekey = NULL;
- /* clear error queue */
- ERR_clear_error();
- }
+ if (privkey != NULL && privkey_method != NULL) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_HAVE_BOTH_PRIVKEY_AND_METHOD);
+ return 0;
}
- EVP_PKEY_free(pubkey);
+ switch (check_leaf_cert_and_privkey(certs[0], privkey)) {
+ case leaf_cert_and_privkey_error:
+ return 0;
+ case leaf_cert_and_privkey_mismatch:
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_AND_PRIVATE_KEY_MISMATCH);
+ return 0;
+ case leaf_cert_and_privkey_ok:
+ break;
+ }
+
+ STACK_OF(CRYPTO_BUFFER) *certs_sk = sk_CRYPTO_BUFFER_new_null();
+ if (certs_sk == NULL) {
+ return 0;
+ }
+
+ for (size_t i = 0; i < num_certs; i++) {
+ if (!sk_CRYPTO_BUFFER_push(certs_sk, certs[i])) {
+ sk_CRYPTO_BUFFER_pop_free(certs_sk, CRYPTO_BUFFER_free);
+ return 0;
+ }
+ CRYPTO_BUFFER_up_ref(certs[i]);
+ }
+
+ EVP_PKEY_free(cert->privatekey);
+ cert->privatekey = privkey;
+ if (privkey != NULL) {
+ EVP_PKEY_up_ref(privkey);
+ }
+ cert->key_method = privkey_method;
+
+ sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
+ cert->chain = certs_sk;
+
+ return 1;
+}
+
+int SSL_set_chain_and_key(SSL *ssl, CRYPTO_BUFFER *const *certs,
+ size_t num_certs, EVP_PKEY *privkey,
+ const SSL_PRIVATE_KEY_METHOD *privkey_method) {
+ return cert_set_chain_and_key(ssl->cert, certs, num_certs, privkey,
+ privkey_method);
+}
+
+int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs,
+ size_t num_certs, EVP_PKEY *privkey,
+ const SSL_PRIVATE_KEY_METHOD *privkey_method) {
+ return cert_set_chain_and_key(ctx->cert, certs, num_certs, privkey,
+ privkey_method);
+}
+
+int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
+ switch (check_leaf_cert_and_privkey(buffer, cert->privatekey)) {
+ case leaf_cert_and_privkey_error:
+ return 0;
+ case leaf_cert_and_privkey_mismatch:
+ /* don't fail for a cert/key mismatch, just free current private key
+ * (when switching to a different cert & key, first this function should
+ * be used, then |ssl_set_pkey|. */
+ EVP_PKEY_free(cert->privatekey);
+ cert->privatekey = NULL;
+ break;
+ case leaf_cert_and_privkey_ok:
+ break;
+ }
cert->x509_method->cert_flush_cached_leaf(cert);
@@ -495,6 +585,12 @@
int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
const EVP_PKEY *privkey) {
+ if (EVP_PKEY_is_opaque(privkey)) {
+ /* We cannot check an opaque private key and have to trust that it
+ * matches. */
+ return 1;
+ }
+
int ret = 0;
switch (EVP_PKEY_cmp(pubkey, privkey)) {
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 4a7459f..4ee3c12 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -1254,10 +1254,10 @@
return 1;
}
-STACK_OF(SSL_CIPHER) *
-ssl_create_cipher_list(const SSL_PROTOCOL_METHOD *ssl_method,
- struct ssl_cipher_preference_list_st **out_cipher_list,
- const char *rule_str, int strict) {
+int ssl_create_cipher_list(
+ const SSL_PROTOCOL_METHOD *ssl_method,
+ struct ssl_cipher_preference_list_st **out_cipher_list,
+ const char *rule_str, int strict) {
STACK_OF(SSL_CIPHER) *cipherstack = NULL;
CIPHER_ORDER *co_list = NULL, *head = NULL, *tail = NULL, *curr;
uint8_t *in_group_flags = NULL;
@@ -1266,7 +1266,7 @@
/* Return with error if nothing to do. */
if (rule_str == NULL || out_cipher_list == NULL) {
- return NULL;
+ return 0;
}
/* Now we have to collect the available ciphers from the compiled in ciphers.
@@ -1275,7 +1275,7 @@
co_list = OPENSSL_malloc(sizeof(CIPHER_ORDER) * kCiphersLen);
if (co_list == NULL) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return NULL;
+ return 0;
}
ssl_cipher_collect_ciphers(ssl_method, co_list, &head, &tail);
@@ -1395,7 +1395,14 @@
*out_cipher_list = pref_list;
pref_list = NULL;
- return cipherstack;
+ /* Configuring an empty cipher list is an error but still updates the
+ * output. */
+ if (sk_SSL_CIPHER_num((*out_cipher_list)->ciphers) == 0) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH);
+ return 0;
+ }
+
+ return 1;
err:
OPENSSL_free(co_list);
@@ -1405,7 +1412,7 @@
OPENSSL_free(pref_list->in_group_flags);
}
OPENSSL_free(pref_list);
- return NULL;
+ return 0;
}
uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *cipher) { return cipher->id; }
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 7ead554..d16c952 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -271,11 +271,7 @@
goto err;
}
- ssl_create_cipher_list(ret->method, &ret->cipher_list,
- SSL_DEFAULT_CIPHER_LIST, 1 /* strict */);
- if (ret->cipher_list == NULL ||
- sk_SSL_CIPHER_num(ret->cipher_list->ciphers) <= 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_LIBRARY_HAS_NO_CIPHERS);
+ if (!SSL_CTX_set_strict_cipher_list(ret, SSL_DEFAULT_CIPHER_LIST)) {
goto err2;
}
@@ -401,7 +397,7 @@
SSL_CTX_up_ref(ctx);
ssl->ctx = ctx;
SSL_CTX_up_ref(ctx);
- ssl->initial_ctx = ctx;
+ ssl->session_ctx = ctx;
if (!ssl->ctx->x509_method->ssl_new(ssl)) {
goto err;
@@ -484,7 +480,7 @@
ssl_cert_free(ssl->cert);
OPENSSL_free(ssl->tlsext_hostname);
- SSL_CTX_free(ssl->initial_ctx);
+ SSL_CTX_free(ssl->session_ctx);
OPENSSL_free(ssl->supported_group_list);
OPENSSL_free(ssl->alpn_client_proto_list);
EVP_PKEY_free(ssl->tlsext_channel_id_private);
@@ -914,6 +910,9 @@
case SSL_PRIVATE_KEY_OPERATION:
return SSL_ERROR_WANT_PRIVATE_KEY_OPERATION;
+
+ case SSL_PENDING_TICKET:
+ return SSL_ERROR_PENDING_TICKET;
}
return SSL_ERROR_SYSCALL;
@@ -1492,71 +1491,23 @@
}
int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *str) {
- STACK_OF(SSL_CIPHER) *cipher_list =
- ssl_create_cipher_list(ctx->method, &ctx->cipher_list, str,
- 0 /* not strict */);
- if (cipher_list == NULL) {
- return 0;
- }
-
- /* |ssl_create_cipher_list| may succeed but return an empty cipher list. */
- if (sk_SSL_CIPHER_num(cipher_list) == 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH);
- return 0;
- }
-
- return 1;
+ return ssl_create_cipher_list(ctx->method, &ctx->cipher_list, str,
+ 0 /* not strict */);
}
int SSL_CTX_set_strict_cipher_list(SSL_CTX *ctx, const char *str) {
- STACK_OF(SSL_CIPHER) *cipher_list =
- ssl_create_cipher_list(ctx->method, &ctx->cipher_list, str,
- 1 /* strict */);
- if (cipher_list == NULL) {
- return 0;
- }
-
- /* |ssl_create_cipher_list| may succeed but return an empty cipher list. */
- if (sk_SSL_CIPHER_num(cipher_list) == 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH);
- return 0;
- }
-
- return 1;
+ return ssl_create_cipher_list(ctx->method, &ctx->cipher_list, str,
+ 1 /* strict */);
}
int SSL_set_cipher_list(SSL *ssl, const char *str) {
- STACK_OF(SSL_CIPHER) *cipher_list =
- ssl_create_cipher_list(ssl->ctx->method, &ssl->cipher_list, str,
- 0 /* not strict */);
- if (cipher_list == NULL) {
- return 0;
- }
-
- /* |ssl_create_cipher_list| may succeed but return an empty cipher list. */
- if (sk_SSL_CIPHER_num(cipher_list) == 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH);
- return 0;
- }
-
- return 1;
+ return ssl_create_cipher_list(ssl->ctx->method, &ssl->cipher_list, str,
+ 0 /* not strict */);
}
int SSL_set_strict_cipher_list(SSL *ssl, const char *str) {
- STACK_OF(SSL_CIPHER) *cipher_list =
- ssl_create_cipher_list(ssl->ctx->method, &ssl->cipher_list, str,
- 1 /* strict */);
- if (cipher_list == NULL) {
- return 0;
- }
-
- /* |ssl_create_cipher_list| may succeed but return an empty cipher list. */
- if (sk_SSL_CIPHER_num(cipher_list) == 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH);
- return 0;
- }
-
- return 1;
+ return ssl_create_cipher_list(ssl->ctx->method, &ssl->cipher_list, str,
+ 1 /* strict */);
}
const char *SSL_get_servername(const SSL *ssl, const int type) {
@@ -1598,6 +1549,10 @@
ctx->signed_cert_timestamps_enabled = 1;
}
+void SSL_CTX_i_promise_to_verify_certs_after_the_handshake(SSL_CTX *ctx) {
+ ctx->i_promise_to_verify_certs_after_the_handshake = 1;
+}
+
void SSL_enable_signed_cert_timestamps(SSL *ssl) {
ssl->signed_cert_timestamps_enabled = 1;
}
@@ -1848,7 +1803,7 @@
void ssl_update_cache(SSL_HANDSHAKE *hs, int mode) {
SSL *const ssl = hs->ssl;
- SSL_CTX *ctx = ssl->initial_ctx;
+ SSL_CTX *ctx = ssl->session_ctx;
/* Never cache sessions with empty session IDs. */
if (ssl->s3->established_session->session_id_length == 0 ||
(ctx->session_cache_mode & mode) != mode) {
@@ -2033,7 +1988,7 @@
}
if (ctx == NULL) {
- ctx = ssl->initial_ctx;
+ ctx = ssl->session_ctx;
}
ssl_cert_free(ssl->cert);
@@ -2553,6 +2508,10 @@
ctx->grease_enabled = !!enabled;
}
+int32_t SSL_get_ticket_age_skew(const SSL *ssl) {
+ return ssl->s3->ticket_age_skew;
+}
+
int SSL_clear(SSL *ssl) {
/* In OpenSSL, reusing a client |SSL| with |SSL_clear| causes the previously
* established session to be offered the next time around. wpa_supplicant
@@ -2743,3 +2702,8 @@
int SSL_set_max_version(SSL *ssl, uint16_t version) {
return SSL_set_max_proto_version(ssl, version);
}
+
+void SSL_CTX_set_ticket_aead_method(SSL_CTX *ctx,
+ const SSL_TICKET_AEAD_METHOD *aead_method) {
+ ctx->ticket_aead_method = aead_method;
+}
diff --git a/src/ssl/ssl_privkey.c b/src/ssl/ssl_privkey.c
index c5441de..e988827 100644
--- a/src/ssl/ssl_privkey.c
+++ b/src/ssl/ssl_privkey.c
@@ -64,8 +64,6 @@
#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/type_check.h>
-#include <openssl/x509.h>
-#include <openssl/x509v3.h>
#include "internal.h"
@@ -82,9 +80,7 @@
if (cert->chain != NULL &&
sk_CRYPTO_BUFFER_value(cert->chain, 0) != NULL &&
- /* Sanity-check that the private key and the certificate match, unless
- * the key is opaque (in case of, say, a smartcard). */
- !EVP_PKEY_is_opaque(pkey) &&
+ /* Sanity-check that the private key and the certificate match. */
!ssl_cert_check_private_key(cert, pkey)) {
return 0;
}
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index c9390d2..05ae059 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -535,13 +535,13 @@
if (version >= TLS1_3_VERSION) {
/* TLS 1.3 uses tickets as authenticators, so we are willing to use them for
* longer. */
- session->timeout = ssl->initial_ctx->session_psk_dhe_timeout;
+ session->timeout = ssl->session_ctx->session_psk_dhe_timeout;
session->auth_timeout = SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
} else {
/* TLS 1.2 resumption does not incorporate new key material, so we use a
* much shorter timeout. */
- session->timeout = ssl->initial_ctx->session_timeout;
- session->auth_timeout = ssl->initial_ctx->session_timeout;
+ session->timeout = ssl->session_ctx->session_timeout;
+ session->auth_timeout = ssl->session_ctx->session_timeout;
}
if (is_server) {
@@ -581,16 +581,11 @@
return 0;
}
-int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) {
+static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out,
+ const uint8_t *session_buf,
+ size_t session_len) {
int ret = 0;
- /* Serialize the SSL_SESSION to be encoded into the ticket. */
- uint8_t *session_buf = NULL;
- size_t session_len;
- if (!SSL_SESSION_to_bytes_for_ticket(session, &session_buf, &session_len)) {
- return -1;
- }
-
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);
HMAC_CTX hctx;
@@ -611,7 +606,7 @@
/* Initialize HMAC and cipher contexts. If callback present it does all the
* work otherwise use generated values from parent ctx. */
- SSL_CTX *tctx = ssl->initial_ctx;
+ SSL_CTX *tctx = ssl->session_ctx;
uint8_t iv[EVP_MAX_IV_LENGTH];
uint8_t key_name[16];
if (tctx->tlsext_ticket_key_cb != NULL) {
@@ -667,12 +662,60 @@
ret = 1;
err:
- OPENSSL_free(session_buf);
EVP_CIPHER_CTX_cleanup(&ctx);
HMAC_CTX_cleanup(&hctx);
return ret;
}
+static int ssl_encrypt_ticket_with_method(SSL *ssl, CBB *out,
+ const uint8_t *session_buf,
+ size_t session_len) {
+ const SSL_TICKET_AEAD_METHOD *method = ssl->session_ctx->ticket_aead_method;
+ const size_t max_overhead = method->max_overhead(ssl);
+ const size_t max_out = session_len + max_overhead;
+ if (max_out < max_overhead) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+ return 0;
+ }
+
+ uint8_t *ptr;
+ if (!CBB_reserve(out, &ptr, max_out)) {
+ return 0;
+ }
+
+ size_t out_len;
+ if (!method->seal(ssl, ptr, &out_len, max_out, session_buf, session_len)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_TICKET_ENCRYPTION_FAILED);
+ return 0;
+ }
+
+ if (!CBB_did_write(out, out_len)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session) {
+ /* Serialize the SSL_SESSION to be encoded into the ticket. */
+ uint8_t *session_buf = NULL;
+ size_t session_len;
+ if (!SSL_SESSION_to_bytes_for_ticket(session, &session_buf, &session_len)) {
+ return -1;
+ }
+
+ int ret = 0;
+ if (ssl->session_ctx->ticket_aead_method) {
+ ret = ssl_encrypt_ticket_with_method(ssl, out, session_buf, session_len);
+ } else {
+ ret =
+ ssl_encrypt_ticket_with_cipher_ctx(ssl, out, session_buf, session_len);
+ }
+
+ OPENSSL_free(session_buf);
+ return ret;
+}
+
int ssl_session_is_context_valid(const SSL *ssl, const SSL_SESSION *session) {
if (session == NULL) {
return 0;
@@ -736,27 +779,27 @@
SSL_SESSION *session = NULL;
/* Try the internal cache, if it exists. */
- if (!(ssl->initial_ctx->session_cache_mode &
+ if (!(ssl->session_ctx->session_cache_mode &
SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) {
SSL_SESSION data;
data.ssl_version = ssl->version;
data.session_id_length = session_id_len;
OPENSSL_memcpy(data.session_id, session_id, session_id_len);
- CRYPTO_MUTEX_lock_read(&ssl->initial_ctx->lock);
- session = lh_SSL_SESSION_retrieve(ssl->initial_ctx->sessions, &data);
+ CRYPTO_MUTEX_lock_read(&ssl->session_ctx->lock);
+ session = lh_SSL_SESSION_retrieve(ssl->session_ctx->sessions, &data);
if (session != NULL) {
SSL_SESSION_up_ref(session);
}
/* TODO(davidben): This should probably move it to the front of the list. */
- CRYPTO_MUTEX_unlock_read(&ssl->initial_ctx->lock);
+ CRYPTO_MUTEX_unlock_read(&ssl->session_ctx->lock);
}
/* Fall back to the external cache, if it exists. */
if (session == NULL &&
- ssl->initial_ctx->get_session_cb != NULL) {
+ ssl->session_ctx->get_session_cb != NULL) {
int copy = 1;
- session = ssl->initial_ctx->get_session_cb(ssl, (uint8_t *)session_id,
+ session = ssl->session_ctx->get_session_cb(ssl, (uint8_t *)session_id,
session_id_len, ©);
if (session == NULL) {
@@ -776,16 +819,16 @@
}
/* Add the externally cached session to the internal cache if necessary. */
- if (!(ssl->initial_ctx->session_cache_mode &
+ if (!(ssl->session_ctx->session_cache_mode &
SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
- SSL_CTX_add_session(ssl->initial_ctx, session);
+ SSL_CTX_add_session(ssl->session_ctx, session);
}
}
if (session != NULL &&
!ssl_session_is_time_valid(ssl, session)) {
/* The session was from the cache, so remove it. */
- SSL_CTX_remove_session(ssl->initial_ctx, session);
+ SSL_CTX_remove_session(ssl->session_ctx, session);
SSL_SESSION_free(session);
session = NULL;
}
@@ -811,10 +854,18 @@
SSL_early_callback_ctx_extension_get(
client_hello, TLSEXT_TYPE_session_ticket, &ticket, &ticket_len);
if (tickets_supported && ticket_len > 0) {
- if (!tls_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
- client_hello->session_id,
- client_hello->session_id_len)) {
- return ssl_session_error;
+ switch (ssl_process_ticket(ssl, &session, &renew_ticket, ticket, ticket_len,
+ client_hello->session_id,
+ client_hello->session_id_len)) {
+ case ssl_ticket_aead_success:
+ break;
+ case ssl_ticket_aead_ignore_ticket:
+ assert(session == NULL);
+ break;
+ case ssl_ticket_aead_error:
+ return ssl_session_error;
+ case ssl_ticket_aead_retry:
+ return ssl_session_ticket_retry;
}
} else {
/* The client didn't send a ticket, so the session ID is a real ID. */
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 4180463..6b150e8 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -1229,7 +1229,25 @@
PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
}
-static bssl::UniquePtr<X509> GetChainTestCertificate() {
+static bssl::UniquePtr<CRYPTO_BUFFER> BufferFromPEM(const char *pem) {
+ bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem)));
+ char *name, *header;
+ uint8_t *data;
+ long data_len;
+ if (!PEM_read_bio(bio.get(), &name, &header, &data,
+ &data_len)) {
+ return nullptr;
+ }
+ OPENSSL_free(name);
+ OPENSSL_free(header);
+
+ auto ret = bssl::UniquePtr<CRYPTO_BUFFER>(
+ CRYPTO_BUFFER_new(data, data_len, nullptr));
+ OPENSSL_free(data);
+ return ret;
+}
+
+static bssl::UniquePtr<CRYPTO_BUFFER> GetChainTestCertificateBuffer() {
static const char kCertPEM[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIC0jCCAbqgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEQiBD\n"
@@ -1249,12 +1267,24 @@
"MYgF91UDvVzvnYm6TfseM2+ewKirC00GOrZ7rEcFvtxnKSqYf4ckqfNdSU1Y+RRC\n"
"1ngWZ7Ih\n"
"-----END CERTIFICATE-----\n";
- bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
- return bssl::UniquePtr<X509>(
- PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ return BufferFromPEM(kCertPEM);
}
-static bssl::UniquePtr<X509> GetChainTestIntermediate() {
+static bssl::UniquePtr<X509> X509FromBuffer(
+ bssl::UniquePtr<CRYPTO_BUFFER> buffer) {
+ if (!buffer) {
+ return nullptr;
+ }
+ const uint8_t *derp = CRYPTO_BUFFER_data(buffer.get());
+ return bssl::UniquePtr<X509>(
+ d2i_X509(NULL, &derp, CRYPTO_BUFFER_len(buffer.get())));
+}
+
+static bssl::UniquePtr<X509> GetChainTestCertificate() {
+ return X509FromBuffer(GetChainTestCertificateBuffer());
+}
+
+static bssl::UniquePtr<CRYPTO_BUFFER> GetChainTestIntermediateBuffer() {
static const char kCertPEM[] =
"-----BEGIN CERTIFICATE-----\n"
"MIICwjCCAaqgAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwFDESMBAGA1UEAwwJQyBS\n"
@@ -1273,9 +1303,11 @@
"WhWwgM3P3X95fQ3d7oFPR/bVh0YV+Cf861INwplokXgXQ3/TCQ+HNXeAMWn3JLWv\n"
"XFwk8owk9dq/kQGdndGgy3KTEW4ctPX5GNhf3LJ9Q7dLji4ReQ4=\n"
"-----END CERTIFICATE-----\n";
- bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
- return bssl::UniquePtr<X509>(
- PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+ return BufferFromPEM(kCertPEM);
+}
+
+static bssl::UniquePtr<X509> GetChainTestIntermediate() {
+ return X509FromBuffer(GetChainTestIntermediateBuffer());
}
static bssl::UniquePtr<EVP_PKEY> GetChainTestKey() {
@@ -1320,7 +1352,8 @@
int client_err = SSL_get_error(client, client_ret);
if (client_err != SSL_ERROR_NONE &&
client_err != SSL_ERROR_WANT_READ &&
- client_err != SSL_ERROR_WANT_WRITE) {
+ client_err != SSL_ERROR_WANT_WRITE &&
+ client_err != SSL_ERROR_PENDING_TICKET) {
fprintf(stderr, "Client error: %d\n", client_err);
return false;
}
@@ -1329,7 +1362,8 @@
int server_err = SSL_get_error(server, server_ret);
if (server_err != SSL_ERROR_NONE &&
server_err != SSL_ERROR_WANT_READ &&
- server_err != SSL_ERROR_WANT_WRITE) {
+ server_err != SSL_ERROR_WANT_WRITE &&
+ server_err != SSL_ERROR_PENDING_TICKET) {
fprintf(stderr, "Server error: %d\n", server_err);
return false;
}
@@ -3152,6 +3186,304 @@
EXPECT_EQ(Bytes(der, der_len), Bytes(der3, der3_len));
}
+TEST(SSLTest, SetChainAndKeyMismatch) {
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_with_buffers_method()));
+ ASSERT_TRUE(ctx);
+
+ bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+ ASSERT_TRUE(key);
+ bssl::UniquePtr<CRYPTO_BUFFER> leaf = GetChainTestCertificateBuffer();
+ ASSERT_TRUE(leaf);
+ std::vector<CRYPTO_BUFFER*> chain = {
+ leaf.get(),
+ };
+
+ // Should fail because |GetTestKey| doesn't match the chain-test certificate.
+ ASSERT_FALSE(SSL_CTX_set_chain_and_key(ctx.get(), &chain[0], chain.size(),
+ key.get(), nullptr));
+ ERR_clear_error();
+}
+
+TEST(SSLTest, SetChainAndKey) {
+ bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_with_buffers_method()));
+ ASSERT_TRUE(client_ctx);
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_with_buffers_method()));
+ ASSERT_TRUE(server_ctx);
+
+ bssl::UniquePtr<EVP_PKEY> key = GetChainTestKey();
+ ASSERT_TRUE(key);
+ bssl::UniquePtr<CRYPTO_BUFFER> leaf = GetChainTestCertificateBuffer();
+ ASSERT_TRUE(leaf);
+ bssl::UniquePtr<CRYPTO_BUFFER> intermediate =
+ GetChainTestIntermediateBuffer();
+ ASSERT_TRUE(intermediate);
+ std::vector<CRYPTO_BUFFER*> chain = {
+ leaf.get(), intermediate.get(),
+ };
+ ASSERT_TRUE(SSL_CTX_set_chain_and_key(server_ctx.get(), &chain[0],
+ chain.size(), key.get(), nullptr));
+
+ SSL_CTX_i_promise_to_verify_certs_after_the_handshake(client_ctx.get());
+
+ bssl::UniquePtr<SSL> client, server;
+ ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get(),
+ nullptr /* no session */));
+}
+
+// Configuring the empty cipher list, though an error, should still modify the
+// configuration.
+TEST(SSLTest, EmptyCipherList) {
+ bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+ ASSERT_TRUE(ctx);
+
+ // Initially, the cipher list is not empty.
+ EXPECT_NE(0u, sk_SSL_CIPHER_num(SSL_CTX_get_ciphers(ctx.get())));
+
+ // Configuring the empty cipher list fails.
+ EXPECT_FALSE(SSL_CTX_set_cipher_list(ctx.get(), ""));
+ ERR_clear_error();
+
+ // But the cipher list is still updated to empty.
+ EXPECT_EQ(0u, sk_SSL_CIPHER_num(SSL_CTX_get_ciphers(ctx.get())));
+}
+
+// ssl_test_ticket_aead_failure_mode enumerates the possible ways in which the
+// test |SSL_TICKET_AEAD_METHOD| can fail.
+enum ssl_test_ticket_aead_failure_mode {
+ ssl_test_ticket_aead_ok = 0,
+ ssl_test_ticket_aead_seal_fail,
+ ssl_test_ticket_aead_open_soft_fail,
+ ssl_test_ticket_aead_open_hard_fail,
+};
+
+struct ssl_test_ticket_aead_state {
+ unsigned retry_count;
+ ssl_test_ticket_aead_failure_mode failure_mode;
+};
+
+static int ssl_test_ticket_aead_ex_index_dup(CRYPTO_EX_DATA *to,
+ const CRYPTO_EX_DATA *from,
+ void **from_d, int index,
+ long argl, void *argp) {
+ abort();
+}
+
+static void ssl_test_ticket_aead_ex_index_free(void *parent, void *ptr,
+ CRYPTO_EX_DATA *ad, int index,
+ long argl, void *argp) {
+ auto state = reinterpret_cast<ssl_test_ticket_aead_state*>(ptr);
+ if (state == nullptr) {
+ return;
+ }
+
+ OPENSSL_free(state);
+}
+
+static CRYPTO_once_t g_ssl_test_ticket_aead_ex_index_once = CRYPTO_ONCE_INIT;
+static int g_ssl_test_ticket_aead_ex_index;
+
+static int ssl_test_ticket_aead_get_ex_index() {
+ CRYPTO_once(&g_ssl_test_ticket_aead_ex_index_once, [] {
+ g_ssl_test_ticket_aead_ex_index = SSL_get_ex_new_index(
+ 0, nullptr, nullptr, ssl_test_ticket_aead_ex_index_dup,
+ ssl_test_ticket_aead_ex_index_free);
+ });
+ return g_ssl_test_ticket_aead_ex_index;
+}
+
+static size_t ssl_test_ticket_aead_max_overhead(SSL *ssl) {
+ return 1;
+}
+
+static int ssl_test_ticket_aead_seal(SSL *ssl, uint8_t *out, size_t *out_len,
+ size_t max_out_len, const uint8_t *in,
+ size_t in_len) {
+ auto state = reinterpret_cast<ssl_test_ticket_aead_state *>(
+ SSL_get_ex_data(ssl, ssl_test_ticket_aead_get_ex_index()));
+
+ if (state->failure_mode == ssl_test_ticket_aead_seal_fail ||
+ max_out_len < in_len + 1) {
+ return 0;
+ }
+
+ OPENSSL_memmove(out, in, in_len);
+ out[in_len] = 0xff;
+ *out_len = in_len + 1;
+
+ return 1;
+}
+
+static ssl_ticket_aead_result_t ssl_test_ticket_aead_open(
+ SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out_len,
+ const uint8_t *in, size_t in_len) {
+ auto state = reinterpret_cast<ssl_test_ticket_aead_state *>(
+ SSL_get_ex_data(ssl, ssl_test_ticket_aead_get_ex_index()));
+
+ if (state->retry_count > 0) {
+ state->retry_count--;
+ return ssl_ticket_aead_retry;
+ }
+
+ switch (state->failure_mode) {
+ case ssl_test_ticket_aead_ok:
+ break;
+ case ssl_test_ticket_aead_seal_fail:
+ // If |seal| failed then there shouldn't be any ticket to try and
+ // decrypt.
+ abort();
+ break;
+ case ssl_test_ticket_aead_open_soft_fail:
+ return ssl_ticket_aead_ignore_ticket;
+ case ssl_test_ticket_aead_open_hard_fail:
+ return ssl_ticket_aead_error;
+ }
+
+ if (in_len == 0 || in[in_len - 1] != 0xff) {
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
+ if (max_out_len < in_len - 1) {
+ return ssl_ticket_aead_error;
+ }
+
+ OPENSSL_memmove(out, in, in_len - 1);
+ *out_len = in_len - 1;
+ return ssl_ticket_aead_success;
+}
+
+static const SSL_TICKET_AEAD_METHOD kSSLTestTicketMethod = {
+ ssl_test_ticket_aead_max_overhead,
+ ssl_test_ticket_aead_seal,
+ ssl_test_ticket_aead_open,
+};
+
+static void ConnectClientAndServerWithTicketMethod(
+ bssl::UniquePtr<SSL> *out_client, bssl::UniquePtr<SSL> *out_server,
+ SSL_CTX *client_ctx, SSL_CTX *server_ctx, unsigned retry_count,
+ ssl_test_ticket_aead_failure_mode failure_mode, SSL_SESSION *session) {
+ bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
+ ASSERT_TRUE(client);
+ ASSERT_TRUE(server);
+ SSL_set_connect_state(client.get());
+ SSL_set_accept_state(server.get());
+
+ auto state = reinterpret_cast<ssl_test_ticket_aead_state *>(
+ OPENSSL_malloc(sizeof(ssl_test_ticket_aead_state)));
+ ASSERT_TRUE(state);
+ OPENSSL_memset(state, 0, sizeof(ssl_test_ticket_aead_state));
+ state->retry_count = retry_count;
+ state->failure_mode = failure_mode;
+
+ ASSERT_TRUE(SSL_set_ex_data(server.get(), ssl_test_ticket_aead_get_ex_index(),
+ state));
+
+ SSL_set_session(client.get(), session);
+
+ BIO *bio1, *bio2;
+ ASSERT_TRUE(BIO_new_bio_pair(&bio1, 0, &bio2, 0));
+
+ // SSL_set_bio takes ownership.
+ SSL_set_bio(client.get(), bio1, bio1);
+ SSL_set_bio(server.get(), bio2, bio2);
+
+ if (CompleteHandshakes(client.get(), server.get())) {
+ *out_client = std::move(client);
+ *out_server = std::move(server);
+ } else {
+ out_client->reset();
+ out_server->reset();
+ }
+}
+
+class TicketAEADMethodTest
+ : public ::testing::TestWithParam<testing::tuple<
+ uint16_t, unsigned, ssl_test_ticket_aead_failure_mode>> {};
+
+TEST_P(TicketAEADMethodTest, Resume) {
+ bssl::UniquePtr<X509> cert = GetTestCertificate();
+ ASSERT_TRUE(cert);
+ bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+ ASSERT_TRUE(key);
+
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+ ASSERT_TRUE(server_ctx);
+ bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+ ASSERT_TRUE(client_ctx);
+
+ const uint16_t version = testing::get<0>(GetParam());
+ const unsigned retry_count = testing::get<1>(GetParam());
+ const ssl_test_ticket_aead_failure_mode failure_mode =
+ testing::get<2>(GetParam());
+
+ ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
+ ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
+ ASSERT_TRUE(SSL_CTX_set_min_proto_version(client_ctx.get(), version));
+ ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), version));
+ ASSERT_TRUE(SSL_CTX_set_min_proto_version(server_ctx.get(), version));
+ ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), version));
+
+ SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
+ SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
+ SSL_CTX_set_current_time_cb(client_ctx.get(), FrozenTimeCallback);
+ SSL_CTX_set_current_time_cb(server_ctx.get(), FrozenTimeCallback);
+ SSL_CTX_sess_set_new_cb(client_ctx.get(), SaveLastSession);
+
+ SSL_CTX_set_ticket_aead_method(server_ctx.get(), &kSSLTestTicketMethod);
+
+ bssl::UniquePtr<SSL> client, server;
+ ConnectClientAndServerWithTicketMethod(&client, &server, client_ctx.get(),
+ server_ctx.get(), retry_count,
+ failure_mode, nullptr);
+ switch (failure_mode) {
+ case ssl_test_ticket_aead_ok:
+ case ssl_test_ticket_aead_open_hard_fail:
+ case ssl_test_ticket_aead_open_soft_fail:
+ ASSERT_TRUE(client);
+ break;
+ case ssl_test_ticket_aead_seal_fail:
+ EXPECT_FALSE(client);
+ return;
+ }
+ EXPECT_FALSE(SSL_session_reused(client.get()));
+ EXPECT_FALSE(SSL_session_reused(server.get()));
+
+ // Run the read loop to account for post-handshake tickets in TLS 1.3.
+ SSL_read(client.get(), nullptr, 0);
+
+ bssl::UniquePtr<SSL_SESSION> session = std::move(g_last_session);
+ ConnectClientAndServerWithTicketMethod(&client, &server, client_ctx.get(),
+ server_ctx.get(), retry_count,
+ failure_mode, session.get());
+ switch (failure_mode) {
+ case ssl_test_ticket_aead_ok:
+ ASSERT_TRUE(client);
+ EXPECT_TRUE(SSL_session_reused(client.get()));
+ EXPECT_TRUE(SSL_session_reused(server.get()));
+ break;
+ case ssl_test_ticket_aead_seal_fail:
+ abort();
+ break;
+ case ssl_test_ticket_aead_open_hard_fail:
+ EXPECT_FALSE(client);
+ break;
+ case ssl_test_ticket_aead_open_soft_fail:
+ ASSERT_TRUE(client);
+ EXPECT_FALSE(SSL_session_reused(client.get()));
+ EXPECT_FALSE(SSL_session_reused(server.get()));
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ TicketAEADMethodTests, TicketAEADMethodTest,
+ testing::Combine(
+ testing::Values(TLS1_2_VERSION, TLS1_3_VERSION),
+ testing::Values(0, 1, 2),
+ testing::Values(ssl_test_ticket_aead_ok,
+ ssl_test_ticket_aead_seal_fail,
+ ssl_test_ticket_aead_open_soft_fail,
+ ssl_test_ticket_aead_open_hard_fail)));
+
// TODO(davidben): Convert this file to GTest properly.
TEST(SSLTest, AllTests) {
if (!TestCipherRules() ||
diff --git a/src/ssl/ssl_x509.c b/src/ssl/ssl_x509.c
index 9a396d0..65405aa 100644
--- a/src/ssl/ssl_x509.c
+++ b/src/ssl/ssl_x509.c
@@ -565,6 +565,66 @@
session->x509_chain_without_leaf = NULL;
}
+static int ssl_verify_alarm_type(long type) {
+ switch (type) {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+ return SSL_AD_UNKNOWN_CA;
+
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_CERT_REJECTED:
+ case X509_V_ERR_HOSTNAME_MISMATCH:
+ case X509_V_ERR_EMAIL_MISMATCH:
+ case X509_V_ERR_IP_ADDRESS_MISMATCH:
+ return SSL_AD_BAD_CERTIFICATE;
+
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ return SSL_AD_DECRYPT_ERROR;
+
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ return SSL_AD_CERTIFICATE_EXPIRED;
+
+ case X509_V_ERR_CERT_REVOKED:
+ return SSL_AD_CERTIFICATE_REVOKED;
+
+ case X509_V_ERR_UNSPECIFIED:
+ case X509_V_ERR_OUT_OF_MEM:
+ case X509_V_ERR_INVALID_CALL:
+ case X509_V_ERR_STORE_LOOKUP:
+ return SSL_AD_INTERNAL_ERROR;
+
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ case X509_V_ERR_INVALID_CA:
+ return SSL_AD_UNKNOWN_CA;
+
+ case X509_V_ERR_APPLICATION_VERIFICATION:
+ return SSL_AD_HANDSHAKE_FAILURE;
+
+ case X509_V_ERR_INVALID_PURPOSE:
+ return SSL_AD_UNSUPPORTED_CERTIFICATE;
+
+ default:
+ return SSL_AD_CERTIFICATE_UNKNOWN;
+ }
+}
+
static int ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session,
SSL *ssl) {
STACK_OF(X509) *const cert_chain = session->x509_chain;
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index adc7344..759d87b 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -1957,18 +1957,14 @@
return 1;
}
-int ssl_ext_pre_shared_key_parse_clienthello(SSL_HANDSHAKE *hs,
- SSL_SESSION **out_session,
- CBS *out_binders,
- uint8_t *out_alert,
- CBS *contents) {
- SSL *const ssl = hs->ssl;
+int ssl_ext_pre_shared_key_parse_clienthello(
+ SSL_HANDSHAKE *hs, CBS *out_ticket, CBS *out_binders,
+ uint32_t *out_obfuscated_ticket_age, uint8_t *out_alert, CBS *contents) {
/* We only process the first PSK identity since we don't support pure PSK. */
- uint32_t obfuscated_ticket_age;
- CBS identities, ticket, binders;
+ CBS identities, binders;
if (!CBS_get_u16_length_prefixed(contents, &identities) ||
- !CBS_get_u16_length_prefixed(&identities, &ticket) ||
- !CBS_get_u32(&identities, &obfuscated_ticket_age) ||
+ !CBS_get_u16_length_prefixed(&identities, out_ticket) ||
+ !CBS_get_u32(&identities, out_obfuscated_ticket_age) ||
!CBS_get_u16_length_prefixed(contents, &binders) ||
CBS_len(&binders) == 0 ||
CBS_len(contents) != 0) {
@@ -2014,18 +2010,6 @@
return 0;
}
- /* TODO(svaldez): Check that the ticket_age is valid when attempting to use
- * the PSK for 0-RTT. http://crbug.com/boringssl/113 */
-
- /* TLS 1.3 session tickets are renewed separately as part of the
- * NewSessionTicket. */
- int unused_renew;
- if (!tls_process_ticket(ssl, out_session, &unused_renew, CBS_data(&ticket),
- CBS_len(&ticket), NULL, 0)) {
- *out_alert = SSL_AD_INTERNAL_ERROR;
- return 0;
- }
-
return 1;
}
@@ -3016,9 +3000,9 @@
if (ssl->ctx->tlsext_servername_callback != 0) {
ret = ssl->ctx->tlsext_servername_callback(ssl, &al,
ssl->ctx->tlsext_servername_arg);
- } else if (ssl->initial_ctx->tlsext_servername_callback != 0) {
- ret = ssl->initial_ctx->tlsext_servername_callback(
- ssl, &al, ssl->initial_ctx->tlsext_servername_arg);
+ } else if (ssl->session_ctx->tlsext_servername_callback != 0) {
+ ret = ssl->session_ctx->tlsext_servername_callback(
+ ssl, &al, ssl->session_ctx->tlsext_servername_arg);
}
switch (ret) {
@@ -3046,12 +3030,12 @@
return 1;
}
-int tls_process_ticket(SSL *ssl, SSL_SESSION **out_session,
- int *out_renew_ticket, const uint8_t *ticket,
- size_t ticket_len, const uint8_t *session_id,
- size_t session_id_len) {
- int ret = 1; /* Most errors are non-fatal. */
- SSL_CTX *ssl_ctx = ssl->initial_ctx;
+static enum ssl_ticket_aead_result_t
+ssl_decrypt_ticket_with_cipher_ctx(SSL *ssl, uint8_t **out, size_t *out_len,
+ int *out_renew_ticket, const uint8_t *ticket,
+ size_t ticket_len) {
+ enum ssl_ticket_aead_result_t ret = ssl_ticket_aead_ignore_ticket;
+ const SSL_CTX *const ssl_ctx = ssl->session_ctx;
uint8_t *plaintext = NULL;
HMAC_CTX hmac_ctx;
@@ -3059,23 +3043,12 @@
EVP_CIPHER_CTX cipher_ctx;
EVP_CIPHER_CTX_init(&cipher_ctx);
- *out_renew_ticket = 0;
- *out_session = NULL;
-
- if (SSL_get_options(ssl) & SSL_OP_NO_TICKET) {
- goto done;
- }
-
- if (session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
- goto done;
- }
-
/* Ensure there is room for the key name and the largest IV
* |tlsext_ticket_key_cb| may try to consume. The real limit may be lower, but
* the maximum IV length should be well under the minimum size for the
* session material and HMAC. */
if (ticket_len < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) {
- goto done;
+ goto out;
}
const uint8_t *iv = ticket + SSL_TICKET_KEY_NAME_LEN;
@@ -3084,28 +3057,26 @@
ssl, (uint8_t *)ticket /* name */, (uint8_t *)iv, &cipher_ctx,
&hmac_ctx, 0 /* decrypt */);
if (cb_ret < 0) {
- ret = 0;
- goto done;
- }
- if (cb_ret == 0) {
- goto done;
- }
- if (cb_ret == 2) {
+ ret = ssl_ticket_aead_error;
+ goto out;
+ } else if (cb_ret == 0) {
+ goto out;
+ } else if (cb_ret == 2) {
*out_renew_ticket = 1;
}
} else {
/* Check the key name matches. */
if (OPENSSL_memcmp(ticket, ssl_ctx->tlsext_tick_key_name,
SSL_TICKET_KEY_NAME_LEN) != 0) {
- goto done;
+ goto out;
}
if (!HMAC_Init_ex(&hmac_ctx, ssl_ctx->tlsext_tick_hmac_key,
sizeof(ssl_ctx->tlsext_tick_hmac_key), tlsext_tick_md(),
NULL) ||
!EVP_DecryptInit_ex(&cipher_ctx, EVP_aes_128_cbc(), NULL,
ssl_ctx->tlsext_tick_aes_key, iv)) {
- ret = 0;
- goto done;
+ ret = ssl_ticket_aead_error;
+ goto out;
}
}
size_t iv_len = EVP_CIPHER_CTX_iv_length(&cipher_ctx);
@@ -3115,7 +3086,7 @@
size_t mac_len = HMAC_size(&hmac_ctx);
if (ticket_len < SSL_TICKET_KEY_NAME_LEN + iv_len + 1 + mac_len) {
/* The ticket must be large enough for key name, IV, data, and MAC. */
- goto done;
+ goto out;
}
HMAC_Update(&hmac_ctx, ticket, ticket_len - mac_len);
HMAC_Final(&hmac_ctx, mac, NULL);
@@ -3125,7 +3096,7 @@
mac_ok = 1;
#endif
if (!mac_ok) {
- goto done;
+ goto out;
}
/* Decrypt the session data. */
@@ -3134,8 +3105,8 @@
mac_len;
plaintext = OPENSSL_malloc(ciphertext_len);
if (plaintext == NULL) {
- ret = 0;
- goto done;
+ ret = ssl_ticket_aead_error;
+ goto out;
}
size_t plaintext_len;
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
@@ -3143,24 +3114,89 @@
plaintext_len = ciphertext_len;
#else
if (ciphertext_len >= INT_MAX) {
- goto done;
+ goto out;
}
int len1, len2;
if (!EVP_DecryptUpdate(&cipher_ctx, plaintext, &len1, ciphertext,
(int)ciphertext_len) ||
!EVP_DecryptFinal_ex(&cipher_ctx, plaintext + len1, &len2)) {
- ERR_clear_error(); /* Don't leave an error on the queue. */
- goto done;
+ ERR_clear_error();
+ goto out;
}
- plaintext_len = (size_t)(len1 + len2);
+ plaintext_len = (size_t)(len1) + len2;
#endif
+ *out = plaintext;
+ plaintext = NULL;
+ *out_len = plaintext_len;
+ ret = ssl_ticket_aead_success;
+
+out:
+ OPENSSL_free(plaintext);
+ HMAC_CTX_cleanup(&hmac_ctx);
+ EVP_CIPHER_CTX_cleanup(&cipher_ctx);
+ return ret;
+}
+
+static enum ssl_ticket_aead_result_t ssl_decrypt_ticket_with_method(
+ SSL *ssl, uint8_t **out, size_t *out_len, int *out_renew_ticket,
+ const uint8_t *ticket, size_t ticket_len) {
+ uint8_t *plaintext = OPENSSL_malloc(ticket_len);
+ if (plaintext == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return ssl_ticket_aead_error;
+ }
+
+ size_t plaintext_len;
+ const enum ssl_ticket_aead_result_t result =
+ ssl->session_ctx->ticket_aead_method->open(
+ ssl, plaintext, &plaintext_len, ticket_len, ticket, ticket_len);
+
+ if (result == ssl_ticket_aead_success) {
+ *out = plaintext;
+ plaintext = NULL;
+ *out_len = plaintext_len;
+ }
+
+ OPENSSL_free(plaintext);
+ return result;
+}
+
+enum ssl_ticket_aead_result_t ssl_process_ticket(
+ SSL *ssl, SSL_SESSION **out_session, int *out_renew_ticket,
+ const uint8_t *ticket, size_t ticket_len, const uint8_t *session_id,
+ size_t session_id_len) {
+ *out_renew_ticket = 0;
+ *out_session = NULL;
+
+ if ((SSL_get_options(ssl) & SSL_OP_NO_TICKET) ||
+ session_id_len > SSL_MAX_SSL_SESSION_ID_LENGTH) {
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
+ uint8_t *plaintext = NULL;
+ size_t plaintext_len;
+ enum ssl_ticket_aead_result_t result;
+ if (ssl->session_ctx->ticket_aead_method != NULL) {
+ result = ssl_decrypt_ticket_with_method(
+ ssl, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len);
+ } else {
+ result = ssl_decrypt_ticket_with_cipher_ctx(
+ ssl, &plaintext, &plaintext_len, out_renew_ticket, ticket, ticket_len);
+ }
+
+ if (result != ssl_ticket_aead_success) {
+ return result;
+ }
+
/* Decode the session. */
SSL_SESSION *session =
SSL_SESSION_from_bytes(plaintext, plaintext_len, ssl->ctx);
+ OPENSSL_free(plaintext);
+
if (session == NULL) {
ERR_clear_error(); /* Don't leave an error on the queue. */
- goto done;
+ return ssl_ticket_aead_ignore_ticket;
}
/* Copy the client's session ID into the new session, to denote the ticket has
@@ -3169,12 +3205,7 @@
session->session_id_length = session_id_len;
*out_session = session;
-
-done:
- OPENSSL_free(plaintext);
- HMAC_CTX_cleanup(&hmac_ctx);
- EVP_CIPHER_CTX_cleanup(&cipher_ctx);
- return ret;
+ return ssl_ticket_aead_success;
}
int tls1_parse_peer_sigalgs(SSL_HANDSHAKE *hs, const CBS *in_sigalgs) {
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 984136c..804cbbb 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -1611,6 +1611,13 @@
return false;
}
+ if (is_resume && config->expect_ticket_age_skew != 0 &&
+ SSL_get_ticket_age_skew(ssl) != config->expect_ticket_age_skew) {
+ fprintf(stderr, "Ticket age skew was %" PRId32 ", wanted %d\n",
+ SSL_get_ticket_age_skew(ssl), config->expect_ticket_age_skew);
+ return false;
+ }
+
return true;
}
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index d316ddd..167e872 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -980,6 +980,10 @@
// server receives from the client.
ExpectTicketAge time.Duration
+ // SendTicketAge, if non-zero, is the ticket age to be sent by the
+ // client.
+ SendTicketAge time.Duration
+
// FailIfSessionOffered, if true, causes the server to fail any
// connections where the client offers a non-empty session ID or session
// ticket.
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index eb072ad..bf38c1a 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -270,6 +270,9 @@
// TODO(nharper): Support sending more
// than one PSK identity.
ticketAge := uint32(c.config.time().Sub(session.ticketCreationTime) / time.Millisecond)
+ if c.config.Bugs.SendTicketAge != 0 {
+ ticketAge = uint32(c.config.Bugs.SendTicketAge / time.Millisecond)
+ }
psk := pskIdentity{
ticket: ticket,
obfuscatedTicketAge: session.ticketAgeAdd + ticketAge,
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 9b4bff8..64edd01 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -342,19 +342,23 @@
}
}
- if config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
- hs.clientHello.signatureAlgorithms = config.signSignatureAlgorithms()
- }
- if config.Bugs.IgnorePeerCurvePreferences {
- hs.clientHello.supportedCurves = config.curvePreferences()
- }
- if config.Bugs.IgnorePeerCipherPreferences {
- hs.clientHello.cipherSuites = config.cipherSuites()
- }
+ applyBugsToClientHello(hs.clientHello, config)
return nil
}
+func applyBugsToClientHello(clientHello *clientHelloMsg, config *Config) {
+ if config.Bugs.IgnorePeerSignatureAlgorithmPreferences {
+ clientHello.signatureAlgorithms = config.signSignatureAlgorithms()
+ }
+ if config.Bugs.IgnorePeerCurvePreferences {
+ clientHello.supportedCurves = config.curvePreferences()
+ }
+ if config.Bugs.IgnorePeerCipherPreferences {
+ clientHello.cipherSuites = config.cipherSuites()
+ }
+}
+
func (hs *serverHandshakeState) doTLS13Handshake() error {
c := hs.c
config := c.config
@@ -587,6 +591,8 @@
}
hs.writeClientHash(newClientHello.marshal())
+ applyBugsToClientHello(newClientHello, config)
+
// Check that the new ClientHello matches the old ClientHello,
// except for relevant modifications.
//
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 55685b0..d90485c 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -8601,6 +8601,38 @@
expectedLocalError: "tls: invalid ticket age",
})
+ // Test that the server's ticket age skew reporting works.
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-TicketAgeSkew-Forward",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendTicketAge: 15 * time.Second,
+ },
+ },
+ resumeSession: true,
+ flags: []string{
+ "-resumption-delay", "10",
+ "-expect-ticket-age-skew", "5",
+ },
+ })
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "TLS13-TicketAgeSkew-Backward",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ Bugs: ProtocolBugs{
+ SendTicketAge: 5 * time.Second,
+ },
+ },
+ resumeSession: true,
+ flags: []string{
+ "-resumption-delay", "10",
+ "-expect-ticket-age-skew", "-5",
+ },
+ })
+
testCases = append(testCases, testCase{
testType: clientTest,
name: "TLS13-SendTicketEarlyDataInfo",
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index fefe376..e581581 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -180,6 +180,7 @@
{ "-resumption-delay", &TestConfig::resumption_delay },
{ "-max-send-fragment", &TestConfig::max_send_fragment },
{ "-read-size", &TestConfig::read_size },
+ { "-expect-ticket-age-skew", &TestConfig::expect_ticket_age_skew },
};
const Flag<std::vector<int>> kIntVectorFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index ee3d462..7057b48 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -133,6 +133,7 @@
int read_size = 0;
bool expect_session_id = false;
bool expect_no_session_id = false;
+ int expect_ticket_age_skew = 0;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.c
index 5bd58eb..6243923 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.c
@@ -23,7 +23,6 @@
#include <openssl/mem.h>
#include <openssl/stack.h>
#include <openssl/x509.h>
-#include <openssl/x509v3.h>
#include "../crypto/internal.h"
#include "internal.h"
@@ -80,6 +79,11 @@
hs->wait = ssl_hs_ok;
return -1;
+ case ssl_hs_pending_ticket:
+ ssl->rwstate = SSL_PENDING_TICKET;
+ hs->wait = ssl_hs_ok;
+ return -1;
+
case ssl_hs_ok:
break;
}
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.c
index c0eb135..f13a4f7 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.c
@@ -23,7 +23,6 @@
#include <openssl/err.h>
#include <openssl/mem.h>
#include <openssl/stack.h>
-#include <openssl/x509.h>
#include "../crypto/internal.h"
#include "internal.h"
@@ -259,7 +258,7 @@
/* Resumption incorporates fresh key material, so refresh the timeout. */
ssl_session_renew_timeout(ssl, hs->new_session,
- ssl->initial_ctx->session_psk_dhe_timeout);
+ ssl->session_ctx->session_psk_dhe_timeout);
} else if (!ssl_get_new_session(hs, 0)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index e7cc296..9c8d1a1 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -36,6 +36,7 @@
enum server_hs_state_t {
state_select_parameters = 0,
+ state_select_session,
state_send_hello_retry_request,
state_process_second_client_hello,
state_send_server_hello,
@@ -134,6 +135,8 @@
}
static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) {
+ /* At this point, most ClientHello extensions have already been processed by
+ * the common handshake logic. Resolve the remaining non-PSK parameters. */
SSL *const ssl = hs->ssl;
SSL_CLIENT_HELLO client_hello;
@@ -152,6 +155,14 @@
return ssl_hs_error;
}
+ /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
+ * deferred. Complete it now. */
+ uint8_t alert = SSL_AD_DECODE_ERROR;
+ if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_hs_error;
+ }
+
/* The PRF hash is now known. Set up the key schedule and hash the
* ClientHello. */
if (!tls13_init_key_schedule(hs) ||
@@ -159,94 +170,159 @@
return ssl_hs_error;
}
+ hs->tls13_state = state_select_session;
+ return ssl_hs_ok;
+}
- /* Decode the ticket if we agree on a PSK key exchange mode. */
+static enum ssl_ticket_aead_result_t select_session(
+ SSL_HANDSHAKE *hs, uint8_t *out_alert, SSL_SESSION **out_session,
+ int32_t *out_ticket_age_skew, const SSL_CLIENT_HELLO *client_hello) {
+ SSL *const ssl = hs->ssl;
+ *out_session = NULL;
+
+ /* Decode the ticket if we agreed on a PSK key exchange mode. */
+ CBS pre_shared_key;
+ if (!hs->accept_psk_mode ||
+ !ssl_client_hello_get_extension(client_hello, &pre_shared_key,
+ TLSEXT_TYPE_pre_shared_key)) {
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
+ /* Verify that the pre_shared_key extension is the last extension in
+ * ClientHello. */
+ if (CBS_data(&pre_shared_key) + CBS_len(&pre_shared_key) !=
+ client_hello->extensions + client_hello->extensions_len) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_PRE_SHARED_KEY_MUST_BE_LAST);
+ *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+ return ssl_ticket_aead_error;
+ }
+
+ CBS ticket, binders;
+ uint32_t client_ticket_age;
+ if (!ssl_ext_pre_shared_key_parse_clienthello(hs, &ticket, &binders,
+ &client_ticket_age, out_alert,
+ &pre_shared_key)) {
+ return ssl_ticket_aead_error;
+ }
+
+ /* TLS 1.3 session tickets are renewed separately as part of the
+ * NewSessionTicket. */
+ int unused_renew;
+ SSL_SESSION *session = NULL;
+ enum ssl_ticket_aead_result_t ret =
+ ssl_process_ticket(ssl, &session, &unused_renew, CBS_data(&ticket),
+ CBS_len(&ticket), NULL, 0);
+ switch (ret) {
+ case ssl_ticket_aead_success:
+ break;
+ case ssl_ticket_aead_error:
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ return ret;
+ default:
+ return ret;
+ }
+
+ if (!ssl_session_is_resumable(hs, session) ||
+ /* Historically, some TLS 1.3 tickets were missing ticket_age_add. */
+ !session->ticket_age_add_valid) {
+ SSL_SESSION_free(session);
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
+ /* Recover the client ticket age and convert to seconds. */
+ client_ticket_age -= session->ticket_age_add;
+ client_ticket_age /= 1000;
+
+ struct OPENSSL_timeval now;
+ ssl_get_current_time(ssl, &now);
+
+ /* Compute the server ticket age in seconds. */
+ assert(now.tv_sec >= session->time);
+ uint64_t server_ticket_age = now.tv_sec - session->time;
+
+ /* To avoid overflowing |hs->ticket_age_skew|, we will not resume
+ * 68-year-old sessions. */
+ if (server_ticket_age > INT32_MAX) {
+ SSL_SESSION_free(session);
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
+ /* TODO(davidben,svaldez): Measure this value to decide on tolerance. For
+ * now, accept all values. https://crbug.com/boringssl/113. */
+ *out_ticket_age_skew =
+ (int32_t)client_ticket_age - (int32_t)server_ticket_age;
+
+ /* Check the PSK binder. */
+ if (!tls13_verify_psk_binder(hs, session, &binders)) {
+ SSL_SESSION_free(session);
+ *out_alert = SSL_AD_DECRYPT_ERROR;
+ return ssl_ticket_aead_error;
+ }
+
+ *out_session = session;
+ return ssl_ticket_aead_success;
+}
+
+static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) {
+ SSL *const ssl = hs->ssl;
+ SSL_CLIENT_HELLO client_hello;
+ if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
+ ssl->init_num)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ return ssl_hs_error;
+ }
+
uint8_t alert = SSL_AD_DECODE_ERROR;
SSL_SESSION *session = NULL;
- CBS pre_shared_key, binders;
- if (hs->accept_psk_mode &&
- ssl_client_hello_get_extension(&client_hello, &pre_shared_key,
- TLSEXT_TYPE_pre_shared_key)) {
- /* Verify that the pre_shared_key extension is the last extension in
- * ClientHello. */
- if (CBS_data(&pre_shared_key) + CBS_len(&pre_shared_key) !=
- client_hello.extensions + client_hello.extensions_len) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_PRE_SHARED_KEY_MUST_BE_LAST);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
- return ssl_hs_error;
- }
-
- if (!ssl_ext_pre_shared_key_parse_clienthello(hs, &session, &binders,
- &alert, &pre_shared_key)) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
- return ssl_hs_error;
- }
- }
-
- if (session != NULL &&
- !ssl_session_is_resumable(hs, session)) {
- SSL_SESSION_free(session);
- session = NULL;
- }
-
- /* Set up the new session, either using the original one as a template or
- * creating a fresh one. */
- if (session == NULL) {
- if (!ssl_get_new_session(hs, 1 /* server */)) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
- return ssl_hs_error;
- }
-
- hs->new_session->cipher = hs->new_cipher;
-
- /* On new sessions, stash the SNI value in the session. */
- if (hs->hostname != NULL) {
- OPENSSL_free(hs->new_session->tlsext_hostname);
- hs->new_session->tlsext_hostname = BUF_strdup(hs->hostname);
- if (hs->new_session->tlsext_hostname == NULL) {
+ switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew,
+ &client_hello)) {
+ case ssl_ticket_aead_ignore_ticket:
+ assert(session == NULL);
+ if (!ssl_get_new_session(hs, 1 /* server */)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
- }
- } else {
- /* Check the PSK binder. */
- if (!tls13_verify_psk_binder(hs, session, &binders)) {
- SSL_SESSION_free(session);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
- return ssl_hs_error;
- }
+ break;
- /* Only authentication information carries over in TLS 1.3. */
- hs->new_session = SSL_SESSION_dup(session, SSL_SESSION_DUP_AUTH_ONLY);
- if (hs->new_session == NULL) {
+ case ssl_ticket_aead_success:
+ /* Carry over authentication information from the previous handshake into
+ * a fresh session. */
+ hs->new_session = SSL_SESSION_dup(session, SSL_SESSION_DUP_AUTH_ONLY);
+ SSL_SESSION_free(session);
+ if (hs->new_session == NULL) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+
+ ssl->s3->session_reused = 1;
+
+ /* Resumption incorporates fresh key material, so refresh the timeout. */
+ ssl_session_renew_timeout(ssl, hs->new_session,
+ ssl->session_ctx->session_psk_dhe_timeout);
+ break;
+
+ case ssl_ticket_aead_error:
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
+ return ssl_hs_error;
+
+ case ssl_ticket_aead_retry:
+ hs->tls13_state = state_select_session;
+ return ssl_hs_pending_ticket;
+ }
+
+ /* Record connection properties in the new session. */
+ hs->new_session->cipher = hs->new_cipher;
+
+ if (hs->hostname != NULL) {
+ OPENSSL_free(hs->new_session->tlsext_hostname);
+ hs->new_session->tlsext_hostname = BUF_strdup(hs->hostname);
+ if (hs->new_session->tlsext_hostname == NULL) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
}
- ssl->s3->session_reused = 1;
- SSL_SESSION_free(session);
-
- /* Resumption incorporates fresh key material, so refresh the timeout. */
- ssl_session_renew_timeout(ssl, hs->new_session,
- ssl->initial_ctx->session_psk_dhe_timeout);
}
- if (ssl->ctx->dos_protection_cb != NULL &&
- ssl->ctx->dos_protection_cb(&client_hello) == 0) {
- /* Connection rejected for DOS reasons. */
- OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
- return ssl_hs_error;
- }
-
- /* HTTP/2 negotiation depends on the cipher suite, so ALPN negotiation was
- * deferred. Complete it now. */
- alert = SSL_AD_DECODE_ERROR;
- if (!ssl_negotiate_alpn(hs, &alert, &client_hello)) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
- return ssl_hs_error;
- }
-
- /* Store the initial negotiated ALPN in the session. */
if (ssl->s3->alpn_selected != NULL) {
hs->new_session->early_alpn =
BUF_memdup(ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
@@ -257,6 +333,14 @@
hs->new_session->early_alpn_len = ssl->s3->alpn_selected_len;
}
+ if (ssl->ctx->dos_protection_cb != NULL &&
+ ssl->ctx->dos_protection_cb(&client_hello) == 0) {
+ /* Connection rejected for DOS reasons. */
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_REJECTED);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+ return ssl_hs_error;
+ }
+
/* Incorporate the PSK into the running secret. */
if (ssl->s3->session_reused) {
if (!tls13_advance_key_schedule(hs, hs->new_session->master_key,
@@ -565,6 +649,7 @@
if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
goto err;
}
+ session->ticket_age_add_valid = 1;
CBB body, ticket, extensions;
if (!ssl->method->init_message(ssl, &cbb, &body,
@@ -618,6 +703,9 @@
case state_select_parameters:
ret = do_select_parameters(hs);
break;
+ case state_select_session:
+ ret = do_select_session(hs);
+ break;
case state_send_hello_retry_request:
ret = do_send_hello_retry_request(hs);
break;
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index 7778310..6144f86 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -278,7 +278,14 @@
static void ssl_noop_x509_session_clear(SSL_SESSION *session) {}
static int ssl_noop_x509_session_verify_cert_chain(SSL_SESSION *session,
SSL *ssl) {
- return 0;
+ if (!ssl->ctx->i_promise_to_verify_certs_after_the_handshake) {
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNKNOWN_CA);
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
+ return 0;
+ }
+
+ session->verify_result = X509_V_OK;
+ return 1;
}
static void ssl_noop_x509_hs_flush_cached_ca_names(SSL_HANDSHAKE *hs) {}
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index bf9735c..aafb6f5 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -140,7 +140,7 @@
* state needs record-splitting and zero otherwise. */
static int ssl_needs_record_splitting(const SSL *ssl) {
return ssl->s3->aead_write_ctx != NULL &&
- ssl3_protocol_version(ssl) < TLS1_1_VERSION &&
+ ssl->s3->aead_write_ctx->version < TLS1_1_VERSION &&
(ssl->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 &&
SSL_CIPHER_is_block_cipher(ssl->s3->aead_write_ctx->cipher);
}
@@ -190,8 +190,8 @@
size_t ret = SSL3_RT_HEADER_LENGTH;
ret += SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
/* TLS 1.3 needs an extra byte for the encrypted record type. */
- if (ssl->s3->have_version &&
- ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+ if (ssl->s3->aead_write_ctx != NULL &&
+ ssl->s3->aead_write_ctx->version >= TLS1_3_VERSION) {
ret += 1;
}
if (ssl_needs_record_splitting(ssl)) {
@@ -287,9 +287,8 @@
}
/* TLS 1.3 hides the record type inside the encrypted data. */
- if (ssl->s3->have_version &&
- ssl3_protocol_version(ssl) >= TLS1_3_VERSION &&
- ssl->s3->aead_read_ctx != NULL) {
+ if (ssl->s3->aead_read_ctx != NULL &&
+ ssl->s3->aead_read_ctx->version >= TLS1_3_VERSION) {
/* The outer record type is always application_data. */
if (type != SSL3_RT_APPLICATION_DATA) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_OUTER_RECORD_TYPE);
@@ -357,9 +356,8 @@
assert(!buffers_alias(in, in_len, out, max_out));
/* TLS 1.3 hides the actual record type inside the encrypted data. */
- if (ssl->s3->have_version &&
- ssl3_protocol_version(ssl) >= TLS1_3_VERSION &&
- ssl->s3->aead_write_ctx != NULL) {
+ if (ssl->s3->aead_write_ctx != NULL &&
+ ssl->s3->aead_write_ctx->version >= TLS1_3_VERSION) {
if (in_len > in_len + SSL3_RT_HEADER_LENGTH + 1 ||
max_out < in_len + SSL3_RT_HEADER_LENGTH + 1) {
OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);