summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRobert Sloan <varomodt@google.com>2017-07-10 08:09:18 -0700
committerRobert Sloan <varomodt@google.com>2017-07-10 08:10:17 -0700
commitf6200e70eccb73d7a8a6940d081918f5a2b98fad (patch)
treee431deae90e88f2e82a03954f7542be25c9a74ca /src
parent927a49544eb76fe28bcca2552db0168fd2efc502 (diff)
downloadboringssl-f6200e70eccb73d7a8a6940d081918f5a2b98fad.tar.gz
external/boringssl: Sync to ee7aa02744a78bf4630913b1c83d0fe36aa45efc.android-o-preview-4
This includes the following changes: https://boringssl.googlesource.com/boringssl/+log/a93bc1124c00b1ac0a68ea5cb14b158d6c8366e1..ee7aa02744a78bf4630913b1c83d0fe36aa45efc Test: BoringSSL CTS Presubmits. Change-Id: I5a05899374e616003f841983b6545f5c90e7c71d
Diffstat (limited to 'src')
-rw-r--r--src/FUZZING.md4
-rw-r--r--src/STYLE.md11
-rw-r--r--src/crypto/cipher_extra/aead_test.cc25
-rw-r--r--src/crypto/evp/evp_tests.txt6
-rw-r--r--src/crypto/fipsmodule/FIPS.md69
-rw-r--r--src/crypto/fipsmodule/rsa/padding.c12
-rw-r--r--src/crypto/fipsmodule/rsa/rsa_impl.c6
-rw-r--r--src/crypto/x509/asn1_gen.c37
-rw-r--r--src/crypto/x509v3/pcy_tree.c38
-rw-r--r--src/include/openssl/ssl.h132
-rw-r--r--src/ssl/CMakeLists.txt1
-rw-r--r--src/ssl/dtls_method.c33
-rw-r--r--src/ssl/handshake_client.c39
-rw-r--r--src/ssl/handshake_server.c111
-rw-r--r--src/ssl/internal.h85
-rw-r--r--src/ssl/ssl_asn1.c9
-rw-r--r--src/ssl/ssl_cipher.c17
-rw-r--r--src/ssl/ssl_lib.c196
-rw-r--r--src/ssl/ssl_session.c21
-rw-r--r--src/ssl/ssl_test.cc32
-rw-r--r--src/ssl/ssl_versions.c343
-rw-r--r--src/ssl/t1_lib.c63
-rw-r--r--src/ssl/test/bssl_shim.cc77
-rw-r--r--src/ssl/test/fuzzer.h271
-rw-r--r--src/ssl/test/runner/common.go68
-rw-r--r--src/ssl/test/runner/conn.go1
-rw-r--r--src/ssl/test/runner/dtls.go37
-rw-r--r--src/ssl/test/runner/fuzzer_mode.json4
-rw-r--r--src/ssl/test/runner/handshake_client.go58
-rw-r--r--src/ssl/test/runner/handshake_server.go130
-rw-r--r--src/ssl/test/runner/runner.go419
-rw-r--r--src/ssl/test/test_config.cc3
-rw-r--r--src/ssl/test/test_config.h3
-rw-r--r--src/ssl/tls13_enc.c26
-rw-r--r--src/ssl/tls13_server.c13
-rw-r--r--src/ssl/tls_method.c37
-rw-r--r--src/ssl/tls_record.c6
-rw-r--r--src/tool/client.cc11
-rw-r--r--src/tool/transport_common.cc50
-rw-r--r--src/tool/transport_common.h6
40 files changed, 1718 insertions, 792 deletions
diff --git a/src/FUZZING.md b/src/FUZZING.md
index abf5d975..60457c62 100644
--- a/src/FUZZING.md
+++ b/src/FUZZING.md
@@ -68,13 +68,15 @@ Additionally, if `BORINGSSL_UNSAFE_FUZZER_MODE` is set, BoringSSL will:
* Tickets are unencrypted and the MAC check is performed but ignored.
+* renegotiation\_info checks are ignored.
+
This is to prevent the fuzzer from getting stuck at a cryptographic invariant in the protocol.
## TLS transcripts
The `client` and `server` corpora are seeded from the test suite. The test suite has a `-fuzzer` flag which mirrors the fuzzer mode changes above and a `-deterministic` flag which removes all non-determinism on the Go side. Not all tests pass, so `ssl/test/runner/fuzzer_mode.json` contains the necessary suppressions. The `run_tests` target will pass appropriate command-line flags.
-There are separate corpora, `client_corpus_no_fuzzer_mode` and `server_corpus_no_fuzzer_mode`. These are transcripts for fuzzers with only `BORINGSSL_UNSAFE_DETERMINISTIC_MODE` defined. To build in this mode, pass `-DNO_FUZZER_MODE=1` into CMake. This configuration is run in the same way but without `-fuzzer` and `-shim-path` flags.
+There are separate corpora, `client_corpus_no_fuzzer_mode` and `server_corpus_no_fuzzer_mode`. These are transcripts for fuzzers with only `BORINGSSL_UNSAFE_DETERMINISTIC_MODE` defined. To build in this mode, pass `-DNO_FUZZER_MODE=1` into CMake. This configuration is run in the same way but without `-fuzzer` and `-shim-config` flags.
If both sets of tests pass, refresh the fuzzer corpora with `refresh_ssl_corpora.sh`:
diff --git a/src/STYLE.md b/src/STYLE.md
index 4b377e71..3c74b45c 100644
--- a/src/STYLE.md
+++ b/src/STYLE.md
@@ -207,3 +207,14 @@ return value patterns in legacy functions.
Document private functions in their `internal.h` header or, if static,
where defined.
+
+
+## Build logic
+
+BoringSSL is used by many projects with many different build tools.
+Reimplementing and maintaining build logic in each downstream build is
+cumbersome, so build logic should be avoided where possible. Platform-specific
+files should be excluded by wrapping the contents in `#ifdef`s, rather than
+computing platform-specific file lists. Generated source files such as perlasm
+and `err_data.c` may be used in the standalone CMake build but, for downstream
+builds, they should be pre-generated in `generate_build_files.py`.
diff --git a/src/crypto/cipher_extra/aead_test.cc b/src/crypto/cipher_extra/aead_test.cc
index cce432c5..949c8002 100644
--- a/src/crypto/cipher_extra/aead_test.cc
+++ b/src/crypto/cipher_extra/aead_test.cc
@@ -580,6 +580,31 @@ TEST_P(PerAEADTest, UnalignedInput) {
Bytes(out + 1, out_len));
}
+TEST_P(PerAEADTest, Overflow) {
+ alignas(64) uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
+ OPENSSL_memset(key, 'K', sizeof(key));
+
+ bssl::ScopedEVP_AEAD_CTX ctx;
+ const size_t max_tag_len = EVP_AEAD_max_tag_len(aead());
+ ASSERT_TRUE(EVP_AEAD_CTX_init_with_direction(ctx.get(), aead(), key,
+ EVP_AEAD_key_length(aead()),
+ max_tag_len, evp_aead_seal));
+
+ uint8_t plaintext[1] = {0};
+ uint8_t ciphertext[1024] = {0};
+ size_t ciphertext_len;
+ // The AEAD must not overflow when calculating the ciphertext length.
+ ASSERT_FALSE(EVP_AEAD_CTX_seal(
+ ctx.get(), ciphertext, &ciphertext_len, sizeof(ciphertext), nullptr, 0,
+ plaintext, std::numeric_limits<size_t>::max() - max_tag_len + 1, nullptr,
+ 0));
+ ERR_clear_error();
+
+ // (Can't test the scatter interface because it'll attempt to zero the output
+ // buffer on error and the primary output buffer is implicitly the same size
+ // as the input.)
+}
+
// Test that EVP_aead_aes_128_gcm and EVP_aead_aes_256_gcm reject empty nonces.
// AES-GCM is not defined for those.
TEST(AEADTest, AESGCMEmptyNonce) {
diff --git a/src/crypto/evp/evp_tests.txt b/src/crypto/evp/evp_tests.txt
index 52b2a2c4..5e69a4ea 100644
--- a/src/crypto/evp/evp_tests.txt
+++ b/src/crypto/evp/evp_tests.txt
@@ -343,7 +343,7 @@ RSAPadding = PSS
PSSSaltLength = 223
Digest = SHA256
Input = "0123456789ABCDEF0123456789ABCDEF"
-Error = DATA_TOO_LARGE_FOR_KEY_SIZE
+Error = DATA_TOO_LARGE
# The salt length is too large for the modulus (verifying).
Verify = RSA-2048
@@ -360,14 +360,14 @@ RSAPadding = PSS
PSSSaltLength = 0
Digest = SHA512
Input = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
-Error = DATA_TOO_LARGE_FOR_KEY_SIZE
+Error = DATA_TOO_LARGE
Sign = RSA-512
RSAPadding = PSS
PSSSaltLength = -2
Digest = SHA512
Input = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
-Error = DATA_TOO_LARGE_FOR_KEY_SIZE
+Error = DATA_TOO_LARGE
# The hash is too large for the modulus (verifying).
Verify = RSA-512
diff --git a/src/crypto/fipsmodule/FIPS.md b/src/crypto/fipsmodule/FIPS.md
index 06ef9fb5..30c44874 100644
--- a/src/crypto/fipsmodule/FIPS.md
+++ b/src/crypto/fipsmodule/FIPS.md
@@ -1,10 +1,57 @@
+# FIPS 140-2
+
+BoringSSL as a whole is not FIPS validated. However, there is a core library (called BoringCrypto) that is undergoing validation at time of writing. This document contains some notes about the design of the FIPS module and some documentation on performing FIPS-related tasks. This is not a substitute for reading the offical Security Policy (which, at the time of writing, has not yet been published).
+
+## Running CAVP tests
+
+CAVP results are calculated by `fipstools/cavp`, but that binary is almost always run by `fipstools/run_cavp.go`. The latter knows the set of tests to be processed and the flags needed to configure `cavp` for each one. It must be run from the top of a CAVP directory and needs the following options:
+
+1. `-oracle-bin`: points to the location of `fipstools/cavp`
+2. `-no-fax`: this is needed to suppress checking of the FAX files, which are only included in sample sets.
+
+## Breaking power-on and continuous tests
+
+In order to demonstrate failures of the various FIPS 140 tests, BoringSSL can be built in ways that will trigger such failures. This is controlled by passing `-DFIPS_BREAK_TEST=`(test to break) to CMake, where the following tests can be specified:
+
+1. AES\_CBC
+1. AES\_GCM
+1. DES
+1. SHA\_1
+1. SHA\_256
+1. SHA\_512
+1. RSA\_SIG
+1. ECDSA\_SIG
+1. DRBG
+1. RSA\_PWCT
+1. ECDSA\_PWCT
+
+## Breaking the integrity test
+
+The utility in `util/fipstools/break-hash.go` can be used to corrupt the FIPS module inside a binary and thus trigger a failure of the integrity test. Note that the binary must not be stripped, otherwise the utility will not be able to find the FIPS module.
+
+## RNG design
+
+FIPS 140-2 requires that one of its PRNGs be used (which they call DRBGs). In BoringCrypto, we use CTR-DRBG with AES-256 exclusively and `RAND_bytes` (the primary interface for the rest of the system to get random data) takes its output from there.
+
+The DRBG state is kept in a thread-local structure and is seeded from one of the following entropy sources in preference order: RDRAND (on Intel chips), `getrandom`, and `/dev/urandom`. In the case of `/dev/urandom`, in order to ensure that the system has a minimum level of entropy, BoringCrypto polls the kernel until the estimated entropy is at least 256 bits. This is a poor man's version of `getrandom` and we strongly recommend using a kernel recent enough to support the real thing.
+
+In FIPS mode, each of those entropy sources is subject to a 10× overread. That is, when *n* bytes of entropy are needed, *10n* bytes will be read from the entropy source and XORed down to *n* bytes. Reads from the entropy source are also processed in blocks of 16 bytes and if two consecutive chunks are equal the process will abort.
+
+The CTR-DRBG is reseeded every 4096 calls to `RAND_bytes`. Thus the process will randomly crash about every 2¹³⁵ calls.
+
+The FIPS PRNGs allow “additional input” to be fed into a given call. We use this feature to be as robust as possible to state duplication from process forks and VM copies: for every call we read 32 bytes of “additional data” from the entropy source (without overread) which means that cloned states will diverge at the next call to `RAND_bytes`. This is called “prediction resistance” by FIPS, but we do *not* claim this property in a FIPS context because we don't implement it the way they want.
+
+There is a second interface to the RNG which allows the caller to supply bytes that will be XORed into the generated additional data (`RAND_bytes_with_additional_data`). This is used in the ECDSA code to include the message and private key in the generation of *k*, the ECDSA nonce. This allows ECDSA to be robust to entropy failures while still following the FIPS rules.
+
+FIPS requires that RNG state be zeroed when the process exits. In order to implement this, all per-thread RNG states are tracked in a linked list and a destructor function is included which clears them. In order for this to be safe in the presence of threads, a lock is used to stop all other threads from using the RNG once this process has begun. Thus the main thread exiting may cause other threads to deadlock, and drawing on entropy in a destructor function may also deadlock.
+
## Integrity Test
FIPS-140 mandates that a module calculate an HMAC of its own code in a constructor function and compare the result to a known-good value. Typical code produced by a C compiler includes large numbers of relocations: places in the machine code where the linker needs to resolve and inject the final value of a symbolic expression. These relocations mean that the bytes that make up any specific bit of code generally aren't known until the final link has completed.
Additionally, because of shared libraries and ASLR, some relocations can only be resolved at run-time, and thus targets of those relocations vary even after the final link.
-BoringSSL is linked (often statically) into a large number of binaries. It would be a significant cost if each of these binaries had to be post-processed in order to calculate the known-good HMAC value. We would much prefer if the value could be calculated, once, when BoringSSL itself is compiled.
+BoringCrypto is linked (often statically) into a large number of binaries. It would be a significant cost if each of these binaries had to be post-processed in order to calculate the known-good HMAC value. We would much prefer if the value could be calculated, once, when BoringCrypto itself is compiled.
In order for the value to be calculated before the final link, there can be no relocations in the hashed code and data. This document describes how we build C and assembly code in order to produce an object file containing all the code and data for the FIPS module without that code having any relocations.
@@ -32,7 +79,7 @@ One special case is `rel.ro` data, which is data that contains function pointers
Mutable data is a problem. It cannot be in the text segment because the text segment is mapped read-only. If it's in a different segment then the code cannot reference it with a known, IP-relative offset because the segment layout is only fixed during the final link.
-Thankfully, mutable data is very rare in our cryptographic code and I hope that we can get it down to just a few variables. In order to allow this we use a similar design to the redirector functions: the code references a symbol that's in the text segment, but out of the module and thus not hashed. A relocation record is emitted to instruct the linker to poke the final offset to the variable in that location. Thus the only change needed is an extra indirection when loading the value.
+In order to allow this we use a similar design to the redirector functions: the code references a symbol that's in the text segment, but out of the module and thus not hashed. A relocation record is emitted to instruct the linker to poke the final offset to the variable in that location. Thus the only change needed is an extra indirection when loading the value.
##### Other transforms
@@ -42,8 +89,7 @@ The script performs a number of other transformations which are worth noting but
1. Various sections, notably `.rodata`, are moved to the `.text` section, inside the module, so module code may reference it without relocations.
1. For each BSS symbol, it generates a function named after that symbol but with `_bss_get` appended, which returns its address.
1. It inserts the labels that delimit the module's code and data (called `module_start` and `module_end` in the diagram above).
-1. It adds a 32-byte, read-only array outside of the module to contain the known-good HMAC value.
-1. It rewrites some "dummy" references to point to those labels and that array. In order to get the C compiler to emit the correct code it's necessary to make it think that it's referencing static functions. This compensates for that trick.
+1. It adds a 64-byte, read-only array outside of the module to contain the known-good HMAC value.
##### Integrity testing
@@ -59,17 +105,16 @@ Initially the known-good value will be incorrect. Another script (`inject-hash.g
OpenSSL's solution to this problem is broadly similar but has a number of differences:
-1. OpenSSL deals with run-time relocations by not hashing parts of the module's data. BoringSSL will eliminate run-time relocations instead and hash everything.
-1. OpenSSL uses `ld -r` (the partial linking mode) to merge a number of object files into their `fipscanister.o`. For BoringSSL, we propose to merge all the C source files by building a single C file that #includes all the others, then we propose to merge the assembly sources by concatenating them to the assembly output from the C compiler.
-1. OpenSSL depends on the link order and inserts two object files, `fips_start.o` and `fips_end.o`, in order to establish the `module_start` and `module_end` values. BoringSSL proposes to simply add labels at the correct places in the assembly.
-1. OpenSSL calculates the hash after the final link and either injects it into the binary or recompiles with the value of the hash passed in as a #define. BoringSSL calculates it prior to the final link and injects it into the object file.
-1. OpenSSL references read-write data directly, since it can know the offsets to it. BoringSSL indirects these loads and stores.
-1. OpenSSL doesn't run the power-on test until `FIPS_module_mode_set` is called, BoringSSL plans to do it in a constructor function. Failure of the test is non-fatal in OpenSSL, BoringSSL plans to crash.
-1. Since the contents of OpenSSL's module change between compilation and use, OpenSSL generates `fipscanister.o.sha1` to check that the compiled object doesn't change before linking. Since BoringSSL's module is fixed after compilation, the final integrity check is unbroken through the linking process.
+1. OpenSSL deals with run-time relocations by not hashing parts of the module's data.
+1. OpenSSL uses `ld -r` (the partial linking mode) to merge a number of object files into their `fipscanister.o`. For BoringCrypto, we merge all the C source files by building a single C file that #includes all the others, and we merge the assembly sources by appending them to the assembly output from the C compiler.
+1. OpenSSL depends on the link order and inserts two object files, `fips_start.o` and `fips_end.o`, in order to establish the `module_start` and `module_end` values. BoringCrypto adds labels at the correct places in the assembly.
+1. OpenSSL calculates the hash after the final link and either injects it into the binary or recompiles with the value of the hash passed in as a #define. BoringCrypto calculates it prior to the final link and injects it into the object file.
+1. OpenSSL references read-write data directly, since it can know the offsets to it. BoringCrypto indirects these loads and stores.
+1. OpenSSL doesn't run the power-on test until `FIPS_module_mode_set` is called. BoringCrypto does it in a constructor function. Failure of the test is non-fatal in OpenSSL, BoringCrypto will crash.
+1. Since the contents of OpenSSL's module change between compilation and use, OpenSSL generates `fipscanister.o.sha1` to check that the compiled object doesn't change before linking. Since BoringCrypto's module is fixed after compilation, the final integrity check is unbroken through the linking process.
Some of the similarities are worth noting:
1. OpenSSL has all out-calls from the module indirecting via the PLT, which is equivalent to the redirector functions described above.
-
![OpenSSL build process](/crypto/fipsmodule/intcheck3.png)
diff --git a/src/crypto/fipsmodule/rsa/padding.c b/src/crypto/fipsmodule/rsa/padding.c
index 69bd9ad6..9f002d24 100644
--- a/src/crypto/fipsmodule/rsa/padding.c
+++ b/src/crypto/fipsmodule/rsa/padding.c
@@ -170,7 +170,7 @@ int RSA_padding_add_PKCS1_type_2(uint8_t *to, size_t to_len,
}
if (from_len > to_len - RSA_PKCS1_PADDING_SIZE) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
return 0;
}
@@ -254,12 +254,12 @@ int RSA_padding_check_PKCS1_type_2(uint8_t *out, size_t *out_len,
int RSA_padding_add_none(uint8_t *to, size_t to_len, const uint8_t *from,
size_t from_len) {
if (from_len > to_len) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
return 0;
}
if (from_len < to_len) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_SMALL_FOR_KEY_SIZE);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_SMALL);
return 0;
}
@@ -330,7 +330,7 @@ int RSA_padding_add_PKCS1_OAEP_mgf1(uint8_t *to, size_t to_len,
size_t emlen = to_len - 1;
if (from_len > emlen - 2 * mdlen - 1) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
return 0;
}
@@ -608,7 +608,7 @@ int RSA_padding_add_PKCS1_PSS_mgf1(RSA *rsa, unsigned char *EM,
}
if (emLen < hLen + 2) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
goto err;
}
@@ -629,7 +629,7 @@ int RSA_padding_add_PKCS1_PSS_mgf1(RSA *rsa, unsigned char *EM,
}
if (emLen - hLen - 2 < sLen) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
goto err;
}
diff --git a/src/crypto/fipsmodule/rsa/rsa_impl.c b/src/crypto/fipsmodule/rsa/rsa_impl.c
index c79f8649..b126164d 100644
--- a/src/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/src/crypto/fipsmodule/rsa/rsa_impl.c
@@ -176,7 +176,7 @@ int RSA_encrypt(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
if (BN_ucmp(f, rsa->n) >= 0) {
/* usually the padding functions would catch this */
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
goto err;
}
@@ -489,7 +489,7 @@ int RSA_verify_raw(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
}
if (BN_ucmp(f, rsa->n) >= 0) {
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
goto err;
}
@@ -563,7 +563,7 @@ int rsa_default_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in,
if (BN_ucmp(f, rsa->n) >= 0) {
/* Usually the padding functions would catch this. */
- OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE_FOR_MODULUS);
+ OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
goto err;
}
diff --git a/src/crypto/x509/asn1_gen.c b/src/crypto/x509/asn1_gen.c
index c52a1ac0..5b74cd1e 100644
--- a/src/crypto/x509/asn1_gen.c
+++ b/src/crypto/x509/asn1_gen.c
@@ -84,6 +84,8 @@
#define ASN1_GEN_STR(str,val) {str, sizeof(str) - 1, val}
#define ASN1_FLAG_EXP_MAX 20
+/* Maximum number of nested sequences */
+#define ASN1_GEN_SEQ_MAX_DEPTH 50
/* Input formats */
@@ -120,13 +122,16 @@ typedef struct {
int exp_count;
} tag_exp_arg;
+static ASN1_TYPE *generate_v3(char *str, X509V3_CTX *cnf, int depth,
+ int *perr);
static int bitstr_cb(const char *elem, int len, void *bitstr);
static int asn1_cb(const char *elem, int len, void *bitstr);
static int append_exp(tag_exp_arg *arg, int exp_tag, int exp_class,
int exp_constructed, int exp_pad, int imp_ok);
static int parse_tagging(const char *vstart, int vlen, int *ptag,
int *pclass);
-static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf);
+static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf,
+ int depth, int *perr);
static ASN1_TYPE *asn1_str2type(const char *str, int format, int utype);
static int asn1_str2tag(const char *tagstr, int len);
@@ -143,6 +148,16 @@ ASN1_TYPE *ASN1_generate_nconf(char *str, CONF *nconf)
ASN1_TYPE *ASN1_generate_v3(char *str, X509V3_CTX *cnf)
{
+ int err = 0;
+ ASN1_TYPE *ret = generate_v3(str, cnf, 0, &err);
+ if (err)
+ OPENSSL_PUT_ERROR(ASN1, err);
+ return ret;
+}
+
+static ASN1_TYPE *generate_v3(char *str, X509V3_CTX *cnf, int depth,
+ int *perr)
+{
ASN1_TYPE *ret;
tag_exp_arg asn1_tags;
tag_exp_type *etmp;
@@ -162,16 +177,22 @@ ASN1_TYPE *ASN1_generate_v3(char *str, X509V3_CTX *cnf)
asn1_tags.imp_class = -1;
asn1_tags.format = ASN1_GEN_FORMAT_ASCII;
asn1_tags.exp_count = 0;
- if (CONF_parse_list(str, ',', 1, asn1_cb, &asn1_tags) != 0)
+ if (CONF_parse_list(str, ',', 1, asn1_cb, &asn1_tags) != 0) {
+ *perr = ASN1_R_UNKNOWN_TAG;
return NULL;
+ }
if ((asn1_tags.utype == V_ASN1_SEQUENCE)
|| (asn1_tags.utype == V_ASN1_SET)) {
if (!cnf) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG);
+ *perr = ASN1_R_SEQUENCE_OR_SET_NEEDS_CONFIG;
return NULL;
}
- ret = asn1_multi(asn1_tags.utype, asn1_tags.str, cnf);
+ if (depth >= ASN1_GEN_SEQ_MAX_DEPTH) {
+ *perr = ASN1_R_ILLEGAL_NESTED_TAGGING;
+ return NULL;
+ }
+ ret = asn1_multi(asn1_tags.utype, asn1_tags.str, cnf, depth, perr);
} else
ret = asn1_str2type(asn1_tags.str, asn1_tags.format, asn1_tags.utype);
@@ -289,7 +310,7 @@ static int asn1_cb(const char *elem, int len, void *bitstr)
int tmp_tag, tmp_class;
if (elem == NULL)
- return 0;
+ return -1;
for (i = 0, p = elem; i < len; p++, i++) {
/* Look for the ':' in name value pairs */
@@ -444,7 +465,8 @@ static int parse_tagging(const char *vstart, int vlen, int *ptag, int *pclass)
/* Handle multiple types: SET and SEQUENCE */
-static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf)
+static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf,
+ int depth, int *perr)
{
ASN1_TYPE *ret = NULL;
STACK_OF(ASN1_TYPE) *sk = NULL;
@@ -463,7 +485,8 @@ static ASN1_TYPE *asn1_multi(int utype, const char *section, X509V3_CTX *cnf)
goto bad;
for (i = 0; i < sk_CONF_VALUE_num(sect); i++) {
ASN1_TYPE *typ =
- ASN1_generate_v3(sk_CONF_VALUE_value(sect, i)->value, cnf);
+ generate_v3(sk_CONF_VALUE_value(sect, i)->value, cnf,
+ depth + 1, perr);
if (!typ)
goto bad;
if (!sk_ASN1_TYPE_push(sk, typ))
diff --git a/src/crypto/x509v3/pcy_tree.c b/src/crypto/x509v3/pcy_tree.c
index 71f9a6dc..256fe88e 100644
--- a/src/crypto/x509v3/pcy_tree.c
+++ b/src/crypto/x509v3/pcy_tree.c
@@ -136,11 +136,14 @@ static void tree_print(char *str, X509_POLICY_TREE *tree,
#endif
-/*
- * Initialize policy tree. Return values: 0 Some internal error occured. -1
- * Inconsistent or invalid extensions in certificates. 1 Tree initialized
- * OK. 2 Policy tree is empty. 5 Tree OK and requireExplicitPolicy true. 6
- * Tree empty and requireExplicitPolicy true.
+/*-
+ * Initialize policy tree. Return values:
+ * 0 Some internal error occurred.
+ * -1 Inconsistent or invalid extensions in certificates.
+ * 1 Tree initialized OK.
+ * 2 Policy tree is empty.
+ * 5 Tree OK and requireExplicitPolicy true.
+ * 6 Tree empty and requireExplicitPolicy true.
*/
static int tree_init(X509_POLICY_TREE **ptree, STACK_OF(X509) *certs,
@@ -720,10 +723,13 @@ void X509_policy_tree_free(X509_POLICY_TREE *tree)
}
-/*
- * Application policy checking function. Return codes: 0 Internal Error. 1
- * Successful. -1 One or more certificates contain invalid or inconsistent
- * extensions -2 User constrained policy set empty and requireExplicit true.
+/*-
+ * Application policy checking function.
+ * Return codes:
+ * 0 Internal Error.
+ * 1 Successful.
+ * -1 One or more certificates contain invalid or inconsistent extensions
+ * -2 User constrained policy set empty and requireExplicit true.
*/
int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy,
@@ -731,6 +737,7 @@ int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy,
STACK_OF(ASN1_OBJECT) *policy_oids, unsigned int flags)
{
int ret;
+ int calc_ret;
X509_POLICY_TREE *tree = NULL;
STACK_OF(X509_POLICY_NODE) *nodes, *auth_nodes = NULL;
*ptree = NULL;
@@ -799,17 +806,20 @@ int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy,
/* Tree is not empty: continue */
- ret = tree_calculate_authority_set(tree, &auth_nodes);
+ calc_ret = tree_calculate_authority_set(tree, &auth_nodes);
- if (!ret)
+ if (!calc_ret)
goto error;
- if (!tree_calculate_user_set(tree, policy_oids, auth_nodes))
- goto error;
+ ret = tree_calculate_user_set(tree, policy_oids, auth_nodes);
- if (ret == 2)
+ if (calc_ret == 2)
sk_X509_POLICY_NODE_free(auth_nodes);
+ if (!ret)
+ goto error;
+
+
if (tree)
*ptree = tree;
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index b5f1c574..59fbeb87 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -1706,25 +1706,39 @@ OPENSSL_EXPORT int SSL_SESSION_set1_id_context(SSL_SESSION *session,
/* Session caching.
*
- * Session caching allows clients to reconnect to a server based on saved
- * parameters from a previous connection.
+ * Session caching allows connections to be established more efficiently based
+ * on saved parameters from a previous connection, called a session (see
+ * |SSL_SESSION|). The client offers a saved session, using an opaque identifier
+ * from a previous connection. The server may accept the session, if it has the
+ * parameters available. Otherwise, it will decline and continue with a full
+ * handshake.
+ *
+ * This requires both the client and the server to retain session state. A
+ * client does so with a stateful session cache. A server may do the same or, if
+ * supported by both sides, statelessly using session tickets. For more
+ * information on the latter, see the next section.
*
* For a server, the library implements a built-in internal session cache as an
- * in-memory hash table. One may also register callbacks to implement a custom
- * external session cache. An external cache may be used in addition to or
- * instead of the internal one. Use |SSL_CTX_set_session_cache_mode| to toggle
- * the internal cache.
- *
- * For a client, the only option is an external session cache. Prior to
- * handshaking, the consumer should look up a session externally (keyed, for
- * instance, by hostname) and use |SSL_set_session| to configure which session
- * to offer. The callbacks may be used to determine when new sessions are
- * available.
- *
- * Note that offering or accepting a session short-circuits most parameter
- * negotiation. Resuming sessions across different configurations may result in
- * surprising behavior. So, for instance, a client implementing a version
- * fallback should shard its session cache by maximum protocol version. */
+ * in-memory hash table. Servers may also use |SSL_CTX_sess_set_get_cb| and
+ * |SSL_CTX_sess_set_new_cb| to implement a custom external session cache. In
+ * particular, this may be used to share a session cache between multiple
+ * servers in a large deployment. An external cache may be used in addition to
+ * or instead of the internal one. Use |SSL_CTX_set_session_cache_mode| to
+ * toggle the internal cache.
+ *
+ * For a client, the only option is an external session cache. Clients may use
+ * |SSL_CTX_sess_set_new_cb| to register a callback for when new sessions are
+ * available. These may be cached and, in subsequent compatible connections,
+ * configured with |SSL_set_session|.
+ *
+ * Note that offering or accepting a session short-circuits certificate
+ * verification and most parameter negotiation. Resuming sessions across
+ * different contexts may result in security failures and surprising
+ * behavior. For a typical client, this means sessions for different hosts must
+ * be cached under different keys. A client that connects to the same host with,
+ * e.g., different cipher suite settings or client certificates should also use
+ * separate session caches between those contexts. Servers should also partition
+ * session caches between SNI hosts with |SSL_CTX_set_session_id_context|. */
/* SSL_SESS_CACHE_OFF disables all session caching. */
#define SSL_SESS_CACHE_OFF 0x0000
@@ -1771,25 +1785,6 @@ OPENSSL_EXPORT int SSL_CTX_get_session_cache_mode(const SSL_CTX *ctx);
* It is an error to call this function after the handshake has begun. */
OPENSSL_EXPORT int SSL_set_session(SSL *ssl, SSL_SESSION *session);
-/* SSL_get_session returns a non-owning pointer to |ssl|'s session. For
- * historical reasons, which session it returns depends on |ssl|'s state.
- *
- * Prior to the start of the initial handshake, it returns the session the
- * caller set with |SSL_set_session|. After the initial handshake has finished
- * and if no additional handshakes are in progress, it returns the currently
- * active session. Its behavior is undefined while a handshake is in progress.
- *
- * Using this function to add new sessions to an external session cache is
- * deprecated. Use |SSL_CTX_sess_set_new_cb| instead. */
-OPENSSL_EXPORT SSL_SESSION *SSL_get_session(const SSL *ssl);
-
-/* SSL_get0_session is an alias for |SSL_get_session|. */
-#define SSL_get0_session SSL_get_session
-
-/* SSL_get1_session acts like |SSL_get_session| but returns a new reference to
- * the session. */
-OPENSSL_EXPORT SSL_SESSION *SSL_get1_session(SSL *ssl);
-
/* SSL_DEFAULT_SESSION_TIMEOUT is the default lifetime, in seconds, of a
* session in TLS 1.2 or earlier. This is how long we are willing to use the
* secret to encrypt traffic without fresh key material. */
@@ -1824,11 +1819,7 @@ OPENSSL_EXPORT uint32_t SSL_CTX_get_timeout(const SSL_CTX *ctx);
* connection without a matching session ID context.
*
* For a server, if |SSL_VERIFY_PEER| is enabled, it is an error to not set a
- * session ID context.
- *
- * TODO(davidben): Is that check needed? That seems a special case of taking
- * care not to cross-resume across configuration changes, and this is only
- * relevant if a server requires client auth. */
+ * session ID context. */
OPENSSL_EXPORT int SSL_CTX_set_session_id_context(SSL_CTX *ctx,
const uint8_t *sid_ctx,
size_t sid_ctx_len);
@@ -1889,12 +1880,7 @@ OPENSSL_EXPORT void SSL_CTX_flush_sessions(SSL_CTX *ctx, uint64_t time);
* ticket is renewed. Further, it may not be called until some time after
* |SSL_do_handshake| or |SSL_connect| completes if False Start is enabled. Thus
* it's recommended to use this callback over checking |SSL_session_reused| on
- * handshake completion.
- *
- * TODO(davidben): Conditioning callbacks on |SSL_SESS_CACHE_CLIENT| or
- * |SSL_SESS_CACHE_SERVER| doesn't make any sense when one could just as easily
- * not supply the callbacks. Removing that condition and the client internal
- * cache would simplify things. */
+ * handshake completion. */
OPENSSL_EXPORT void SSL_CTX_sess_set_new_cb(
SSL_CTX *ctx, int (*new_session_cb)(SSL *ssl, SSL_SESSION *session));
@@ -1959,23 +1945,36 @@ OPENSSL_EXPORT SSL_SESSION *SSL_magic_pending_session_ptr(void);
/* Session tickets.
*
* Session tickets, from RFC 5077, allow session resumption without server-side
- * state. Session tickets are supported in by default but may be disabled with
+ * state. The server maintains a secret ticket key and sends the client opaque
+ * encrypted session parameters, called a ticket. When offering the session, the
+ * client sends the ticket which the server decrypts to recover session state.
+ * Session tickets are enabled by default but may be disabled with
* |SSL_OP_NO_TICKET|.
*
* On the client, ticket-based sessions use the same APIs as ID-based tickets.
* Callers do not need to handle them differently.
*
* On the server, tickets are encrypted and authenticated with a secret key. By
- * default, an |SSL_CTX| generates a key on creation. Tickets are minted and
- * processed transparently. The following functions may be used to configure a
- * persistent key or implement more custom behavior. There are three levels of
- * customisation possible:
+ * default, an |SSL_CTX| generates a key on creation and uses it for the
+ * lifetime of the |SSL_CTX|. Tickets are minted and processed
+ * transparently. The following functions may be used to configure a persistent
+ * key or implement more custom behavior, including key rotation and sharing
+ * keys between multiple servers in a large deployment. There are three levels
+ * of customisation possible:
*
* 1) One can simply set the keys with |SSL_CTX_set_tlsext_ticket_keys|.
* 2) One can configure an |EVP_CIPHER_CTX| and |HMAC_CTX| directly for
* encryption and authentication.
* 3) One can configure an |SSL_TICKET_ENCRYPTION_METHOD| to have more control
- * and the option of asynchronous decryption. */
+ * and the option of asynchronous decryption.
+ *
+ * An attacker that compromises a server's session ticket key can impersonate
+ * the server and, prior to TLS 1.3, retroactively decrypt all application
+ * traffic from sessions using that ticket key. Thus ticket keys must be
+ * regularly rotated for forward secrecy. Note the default key is currently not
+ * rotated.
+ *
+ * TODO(davidben): This is silly. Rotate the default key automatically. */
/* SSL_CTX_get_tlsext_ticket_keys writes |ctx|'s session ticket key material to
* |len| bytes of |out|. It returns one on success and zero if |len| is not
@@ -2167,8 +2166,8 @@ OPENSSL_EXPORT const char *SSL_get_curve_name(uint16_t curve_id);
#define SSL_VERIFY_PEER 0x01
/* SSL_VERIFY_FAIL_IF_NO_PEER_CERT configures a server to reject connections if
- * the client declines to send a certificate. Otherwise |SSL_VERIFY_PEER| still
- * allows anonymous clients. */
+ * the client declines to send a certificate. This flag must be used together
+ * with |SSL_VERIFY_PEER|, otherwise it won't work. */
#define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02
/* SSL_VERIFY_PEER_IF_NO_OBC configures a server to request a client certificate
@@ -3884,6 +3883,29 @@ OPENSSL_EXPORT long BIO_set_ssl(BIO *bio, SSL *ssl, int take_owership);
/* SSL_set_ecdh_auto returns one. */
#define SSL_set_ecdh_auto(ssl, onoff) 1
+/* SSL_get_session returns a non-owning pointer to |ssl|'s session. For
+ * historical reasons, which session it returns depends on |ssl|'s state.
+ *
+ * Prior to the start of the initial handshake, it returns the session the
+ * caller set with |SSL_set_session|. After the initial handshake has finished
+ * and if no additional handshakes are in progress, it returns the currently
+ * active session. Its behavior is undefined while a handshake is in progress.
+ *
+ * If trying to add new sessions to an external session cache, use
+ * |SSL_CTX_sess_set_new_cb| instead. In particular, using the callback is
+ * required as of TLS 1.3. For compatibility, this function will return an
+ * unresumable session which may be cached, but will never be resumed.
+ *
+ * If querying properties of the connection, use APIs on the |SSL| object. */
+OPENSSL_EXPORT SSL_SESSION *SSL_get_session(const SSL *ssl);
+
+/* SSL_get0_session is an alias for |SSL_get_session|. */
+#define SSL_get0_session SSL_get_session
+
+/* SSL_get1_session acts like |SSL_get_session| but returns a new reference to
+ * the session. */
+OPENSSL_EXPORT SSL_SESSION *SSL_get1_session(SSL *ssl);
+
/* Private structures.
*
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index 3e9cd402..0c09443d 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -29,6 +29,7 @@ add_library(
ssl_session.c
ssl_stat.c
ssl_transcript.c
+ ssl_versions.c
ssl_x509.c
t1_enc.c
t1_lib.c
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.c
index 60847895..dd8d7865 100644
--- a/src/ssl/dtls_method.c
+++ b/src/ssl/dtls_method.c
@@ -66,35 +66,6 @@
#include "internal.h"
-static int dtls1_version_from_wire(uint16_t *out_version,
- uint16_t wire_version) {
- switch (wire_version) {
- case DTLS1_VERSION:
- /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */
- *out_version = TLS1_1_VERSION;
- return 1;
- case DTLS1_2_VERSION:
- *out_version = TLS1_2_VERSION;
- return 1;
- }
-
- return 0;
-}
-
-static uint16_t dtls1_version_to_wire(uint16_t version) {
- switch (version) {
- case TLS1_1_VERSION:
- /* DTLS 1.0 maps to TLS 1.1, not TLS 1.0. */
- return DTLS1_VERSION;
- case TLS1_2_VERSION:
- return DTLS1_2_VERSION;
- }
-
- /* It is an error to use this function with an invalid version. */
- assert(0);
- return 0;
-}
-
static int dtls1_supports_cipher(const SSL_CIPHER *cipher) {
return cipher->algorithm_enc != SSL_eNULL;
}
@@ -134,10 +105,6 @@ static int dtls1_set_write_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) {
static const SSL_PROTOCOL_METHOD kDTLSProtocolMethod = {
1 /* is_dtls */,
- TLS1_1_VERSION,
- TLS1_2_VERSION,
- dtls1_version_from_wire,
- dtls1_version_to_wire,
dtls1_new,
dtls1_free,
dtls1_get_message,
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index dddf6029..c772f771 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -717,33 +717,26 @@ static int ssl3_send_client_hello(SSL_HANDSHAKE *hs) {
return -1;
}
- uint16_t max_wire_version = ssl->method->version_to_wire(hs->max_version);
- assert(hs->state == SSL3_ST_CW_CLNT_HELLO_A);
- if (!ssl->s3->have_version) {
- ssl->version = max_wire_version;
- }
-
/* Always advertise the ClientHello version from the original maximum version,
* even on renegotiation. The static RSA key exchange uses this field, and
* some servers fail when it changes across handshakes. */
- hs->client_version = max_wire_version;
- if (hs->max_version >= TLS1_3_VERSION) {
- hs->client_version = ssl->method->version_to_wire(TLS1_2_VERSION);
+ if (SSL_is_dtls(hs->ssl)) {
+ hs->client_version =
+ hs->max_version >= TLS1_2_VERSION ? DTLS1_2_VERSION : DTLS1_VERSION;
+ } else {
+ hs->client_version =
+ hs->max_version >= TLS1_2_VERSION ? TLS1_2_VERSION : hs->max_version;
}
/* If the configured session has expired or was created at a disabled
* version, drop it. */
if (ssl->session != NULL) {
- uint16_t session_version;
if (ssl->session->is_server ||
- !ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- (session_version < TLS1_3_VERSION &&
- ssl->session->session_id_length == 0) ||
+ !ssl_supports_version(hs, ssl->session->ssl_version) ||
+ (ssl->session->session_id_length == 0 &&
+ ssl->session->tlsext_ticklen == 0) ||
ssl->session->not_resumable ||
- !ssl_session_is_time_valid(ssl, ssl->session) ||
- session_version < hs->min_version ||
- session_version > hs->max_version) {
+ !ssl_session_is_time_valid(ssl, ssl->session)) {
ssl_set_session(ssl, NULL);
}
}
@@ -798,7 +791,7 @@ static int dtls1_get_hello_verify_request(SSL_HANDSHAKE *hs) {
static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
CBS server_hello, server_random, session_id;
- uint16_t server_wire_version, cipher_suite;
+ uint16_t server_version, cipher_suite;
uint8_t compression_method;
int ret = ssl->method->ssl_get_message(ssl);
@@ -826,15 +819,13 @@ static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
CBS_init(&server_hello, ssl->init_msg, ssl->init_num);
- if (!CBS_get_u16(&server_hello, &server_wire_version)) {
+ if (!CBS_get_u16(&server_hello, &server_version)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
return -1;
}
- uint16_t server_version;
- if (!ssl->method->version_from_wire(&server_version, server_wire_version) ||
- server_version < hs->min_version || server_version > hs->max_version) {
+ if (!ssl_supports_version(hs, server_version)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
return -1;
@@ -842,11 +833,11 @@ static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
assert(ssl->s3->have_version == ssl->s3->initial_handshake_complete);
if (!ssl->s3->have_version) {
- ssl->version = server_wire_version;
+ ssl->version = server_version;
/* At this point, the connection's version is known and ssl->version is
* fixed. Begin enforcing the record-layer version. */
ssl->s3->have_version = 1;
- } else if (server_wire_version != ssl->version) {
+ } else if (server_version != ssl->version) {
OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
return -1;
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index d591c805..64abd5d2 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -471,12 +471,9 @@ static int negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
const SSL_CLIENT_HELLO *client_hello) {
SSL *const ssl = hs->ssl;
assert(!ssl->s3->have_version);
- uint16_t version = 0;
- /* Check supported_versions extension if it is present. */
- CBS supported_versions;
+ CBS supported_versions, versions;
if (ssl_client_hello_get_extension(client_hello, &supported_versions,
TLSEXT_TYPE_supported_versions)) {
- CBS versions;
if (!CBS_get_u8_length_prefixed(&supported_versions, &versions) ||
CBS_len(&supported_versions) != 0 ||
CBS_len(&versions) == 0) {
@@ -484,89 +481,63 @@ static int negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
*out_alert = SSL_AD_DECODE_ERROR;
return 0;
}
-
- /* Choose the newest commonly-supported version advertised by the client.
- * The client orders the versions according to its preferences, but we're
- * not required to honor the client's preferences. */
- int found_version = 0;
- while (CBS_len(&versions) != 0) {
- uint16_t ext_version;
- if (!CBS_get_u16(&versions, &ext_version)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- *out_alert = SSL_AD_DECODE_ERROR;
- return 0;
- }
- if (!ssl->method->version_from_wire(&ext_version, ext_version)) {
- continue;
- }
- if (hs->min_version <= ext_version &&
- ext_version <= hs->max_version &&
- (!found_version || version < ext_version)) {
- version = ext_version;
- found_version = 1;
- }
- }
-
- if (!found_version) {
- goto unsupported_protocol;
- }
} else {
- /* Process ClientHello.version instead. Note that versions beyond (D)TLS 1.2
- * do not use this mechanism. */
+ /* Convert the ClientHello version to an equivalent supported_versions
+ * extension. */
+ static const uint8_t kTLSVersions[] = {
+ 0x03, 0x03, /* TLS 1.2 */
+ 0x03, 0x02, /* TLS 1.1 */
+ 0x03, 0x01, /* TLS 1 */
+ 0x03, 0x00, /* SSL 3 */
+ };
+
+ static const uint8_t kDTLSVersions[] = {
+ 0xfe, 0xfd, /* DTLS 1.2 */
+ 0xfe, 0xff, /* DTLS 1.0 */
+ };
+
+ size_t versions_len = 0;
if (SSL_is_dtls(ssl)) {
if (client_hello->version <= DTLS1_2_VERSION) {
- version = TLS1_2_VERSION;
+ versions_len = 4;
} else if (client_hello->version <= DTLS1_VERSION) {
- version = TLS1_1_VERSION;
- } else {
- goto unsupported_protocol;
+ versions_len = 2;
}
+ CBS_init(&versions, kDTLSVersions + sizeof(kDTLSVersions) - versions_len,
+ versions_len);
} else {
if (client_hello->version >= TLS1_2_VERSION) {
- version = TLS1_2_VERSION;
+ versions_len = 8;
} else if (client_hello->version >= TLS1_1_VERSION) {
- version = TLS1_1_VERSION;
+ versions_len = 6;
} else if (client_hello->version >= TLS1_VERSION) {
- version = TLS1_VERSION;
+ versions_len = 4;
} else if (client_hello->version >= SSL3_VERSION) {
- version = SSL3_VERSION;
- } else {
- goto unsupported_protocol;
+ versions_len = 2;
}
+ CBS_init(&versions, kTLSVersions + sizeof(kTLSVersions) - versions_len,
+ versions_len);
}
+ }
- /* Apply our minimum and maximum version. */
- if (version > hs->max_version) {
- version = hs->max_version;
- }
-
- if (version < hs->min_version) {
- goto unsupported_protocol;
- }
+ if (!ssl_negotiate_version(hs, out_alert, &ssl->version, &versions)) {
+ return 0;
}
+ /* At this point, the connection's version is known and |ssl->version| is
+ * fixed. Begin enforcing the record-layer version. */
+ ssl->s3->have_version = 1;
+
/* Handle FALLBACK_SCSV. */
if (ssl_client_cipher_list_contains_cipher(client_hello,
SSL3_CK_FALLBACK_SCSV & 0xffff) &&
- version < hs->max_version) {
+ ssl3_protocol_version(ssl) < hs->max_version) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INAPPROPRIATE_FALLBACK);
*out_alert = SSL3_AD_INAPPROPRIATE_FALLBACK;
return 0;
}
- hs->client_version = client_hello->version;
- ssl->version = ssl->method->version_to_wire(version);
-
- /* At this point, the connection's version is known and |ssl->version| is
- * fixed. Begin enforcing the record-layer version. */
- ssl->s3->have_version = 1;
-
return 1;
-
-unsupported_protocol:
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
- *out_alert = SSL_AD_PROTOCOL_VERSION;
- return 0;
}
static STACK_OF(SSL_CIPHER) *
@@ -611,12 +582,6 @@ static void ssl_get_compatible_server_ciphers(SSL_HANDSHAKE *hs,
uint32_t *out_mask_k,
uint32_t *out_mask_a) {
SSL *const ssl = hs->ssl;
- if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
- *out_mask_k = SSL_kGENERIC;
- *out_mask_a = SSL_aGENERIC;
- return;
- }
-
uint32_t mask_k = 0;
uint32_t mask_a = 0;
@@ -759,7 +724,7 @@ static int ssl3_process_client_hello(SSL_HANDSHAKE *hs) {
return -1;
}
- /* Load the client random. */
+ hs->client_version = client_hello.version;
if (client_hello.random_len != SSL3_RANDOM_SIZE) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return -1;
@@ -1083,7 +1048,7 @@ static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs) {
/* Determine the group to use. */
uint16_t group_id;
if (!tls1_get_shared_group(hs, &group_id)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_TMP_ECDH_KEY);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
goto err;
}
@@ -1478,7 +1443,7 @@ static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs) {
/* The key exchange state may now be discarded. */
SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
} else if (!(alg_k & SSL_kPSK)) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_TYPE);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
goto err;
}
@@ -1487,7 +1452,7 @@ static int ssl3_get_client_key_exchange(SSL_HANDSHAKE *hs) {
* pre-shared key. */
if (alg_a & SSL_aPSK) {
if (ssl->psk_server_callback == NULL) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_PSK_NO_SERVER_CB);
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
goto err;
}
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index d56c73ba..f6cea7ab 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -163,6 +163,57 @@ extern "C" {
#endif
+typedef struct ssl_handshake_st SSL_HANDSHAKE;
+
+/* Protocol versions.
+ *
+ * Due to DTLS's historical wire version differences and to support multiple
+ * variants of the same protocol during development, we maintain two notions of
+ * version.
+ *
+ * The "version" or "wire version" is the actual 16-bit value that appears on
+ * the wire. It uniquely identifies a version and is also used at API
+ * boundaries. The set of supported versions differs between TLS and DTLS. Wire
+ * versions are opaque values and may not be compared numerically.
+ *
+ * The "protocol version" identifies the high-level handshake variant being
+ * used. DTLS versions map to the corresponding TLS versions. Draft TLS 1.3
+ * variants all map to TLS 1.3. Protocol versions are sequential and may be
+ * compared numerically. */
+
+/* ssl_protocol_version_from_wire sets |*out| to the protocol version
+ * corresponding to wire version |version| and returns one. If |version| is not
+ * a valid TLS or DTLS version, it returns zero.
+ *
+ * Note this simultaneously handles both DTLS and TLS. Use one of the
+ * higher-level functions below for most operations. */
+int ssl_protocol_version_from_wire(uint16_t *out, uint16_t version);
+
+/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
+ * minimum and maximum enabled protocol versions, respectively. */
+int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+ uint16_t *out_max_version);
+
+/* ssl_supports_version returns one if |hs| supports |version| and zero
+ * otherwise. */
+int ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version);
+
+/* ssl_add_supported_versions writes the supported versions of |hs| to |cbb|, in
+ * decreasing preference order. */
+int ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb);
+
+/* ssl_negotiate_version negotiates a common version based on |hs|'s preferences
+ * and the peer preference list in |peer_versions|. On success, it returns one
+ * and sets |*out_version| to the selected version. Otherwise, it returns zero
+ * and sets |*out_alert| to an alert to send. */
+int ssl_negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+ uint16_t *out_version, const CBS *peer_versions);
+
+/* ssl3_protocol_version returns |ssl|'s protocol version. It is an error to
+ * call this function before the version is determined. */
+uint16_t ssl3_protocol_version(const SSL *ssl);
+
+
/* Cipher suites. */
/* Bits for |algorithm_mkey| (key exchange algorithm). */
@@ -542,8 +593,6 @@ enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert,
/* Private key operations. */
-typedef struct ssl_handshake_st SSL_HANDSHAKE;
-
/* ssl_has_private_key returns one if |ssl| has a private key
* configured and zero otherwise. */
int ssl_has_private_key(const SSL *ssl);
@@ -1414,17 +1463,6 @@ struct ssl_method_st {
struct ssl_protocol_method_st {
/* is_dtls is one if the protocol is DTLS and zero otherwise. */
char is_dtls;
- /* min_version is the minimum implemented version. */
- uint16_t min_version;
- /* max_version is the maximum implemented version. */
- uint16_t max_version;
- /* version_from_wire maps |wire_version| to a protocol version. On success, it
- * sets |*out_version| to the result and returns one. If the version is
- * unknown, it returns zero. */
- int (*version_from_wire)(uint16_t *out_version, uint16_t wire_version);
- /* version_to_wire maps |version| to the wire representation. It is an error
- * to call it with an invalid version. */
- uint16_t (*version_to_wire)(uint16_t version);
int (*ssl_new)(SSL *ssl);
void (*ssl_free)(SSL *ssl);
/* ssl_get_message reads the next handshake message. On success, it returns
@@ -1846,7 +1884,7 @@ struct ssl_st {
const SSL_PROTOCOL_METHOD *method;
/* version is the protocol version. */
- int version;
+ uint16_t version;
/* conf_max_version is the maximum acceptable protocol version configured by
* |SSL_set_max_proto_version|. Note this version is normalized in DTLS and is
@@ -2049,10 +2087,12 @@ int ssl_session_is_time_valid(const SSL *ssl, const SSL_SESSION *session);
int ssl_session_is_resumable(const SSL_HANDSHAKE *hs,
const SSL_SESSION *session);
-/* SSL_SESSION_get_digest returns the digest used in |session|. If the digest is
- * invalid, it returns NULL. */
-const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session,
- const SSL *ssl);
+/* SSL_SESSION_protocol_version returns the protocol version associated with
+ * |session|. */
+uint16_t SSL_SESSION_protocol_version(const SSL_SESSION *session);
+
+/* SSL_SESSION_get_digest returns the digest used in |session|. */
+const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session);
void ssl_set_session(SSL *ssl, SSL_SESSION *session);
@@ -2285,15 +2325,6 @@ int ssl_can_write(const SSL *ssl);
/* ssl_can_read returns one if |ssl| is allowed to read and zero otherwise. */
int ssl_can_read(const SSL *ssl);
-/* ssl_get_version_range sets |*out_min_version| and |*out_max_version| to the
- * minimum and maximum enabled protocol versions, respectively. */
-int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
- uint16_t *out_max_version);
-
-/* ssl3_protocol_version returns |ssl|'s protocol version. It is an error to
- * call this function before the version is determined. */
-uint16_t ssl3_protocol_version(const SSL *ssl);
-
void ssl_get_current_time(const SSL *ssl, struct OPENSSL_timeval *out_clock);
/* ssl_reset_error_state resets state for |SSL_get_error|. */
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index f368ff23..cc6a5596 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -576,10 +576,17 @@ SSL_SESSION *SSL_SESSION_parse(CBS *cbs, const SSL_X509_METHOD *x509_method,
CBS session;
uint64_t version, ssl_version;
+ uint16_t unused;
if (!CBS_get_asn1(cbs, &session, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&session, &version) ||
version != kVersion ||
- !CBS_get_asn1_uint64(&session, &ssl_version)) {
+ !CBS_get_asn1_uint64(&session, &ssl_version) ||
+ /* Require sessions have versions valid in either TLS or DTLS. The session
+ * will not be used by the handshake if not applicable, but, for
+ * simplicity, never parse a session that does not pass
+ * |ssl_protocol_version_from_wire|. */
+ ssl_version > UINT16_MAX ||
+ !ssl_protocol_version_from_wire(&unused, ssl_version)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
goto err;
}
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 562c1f30..5d888783 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -1035,7 +1035,7 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method,
uint32_t alg_mkey, alg_auth, alg_enc, alg_mac;
uint16_t min_version;
const char *l, *buf;
- int multi, skip_rule, rule, ok, in_group = 0, has_group = 0;
+ int multi, skip_rule, rule, in_group = 0, has_group = 0;
size_t j, buf_len;
uint32_t cipher_id;
char ch;
@@ -1082,10 +1082,7 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method,
rule = CIPHER_SPECIAL;
l++;
} else if (ch == '[') {
- if (in_group) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NESTED_GROUP);
- return 0;
- }
+ assert(!in_group);
in_group = 1;
has_group = 1;
l++;
@@ -1185,15 +1182,11 @@ static int ssl_cipher_process_rulestr(const SSL_PROTOCOL_METHOD *ssl_method,
/* Ok, we have the rule, now apply it. */
if (rule == CIPHER_SPECIAL) {
- /* special command */
- ok = 0;
- if (buf_len == 8 && !strncmp(buf, "STRENGTH", 8)) {
- ok = ssl_cipher_strength_sort(head_p, tail_p);
- } else {
+ if (buf_len != 8 || strncmp(buf, "STRENGTH", 8) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMMAND);
+ return 0;
}
-
- if (ok == 0) {
+ if (!ssl_cipher_strength_sort(head_p, tail_p)) {
return 0;
}
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 1b002a5e..109dfd07 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -978,60 +978,6 @@ int SSL_get_error(const SSL *ssl, int ret_code) {
return SSL_ERROR_SYSCALL;
}
-static int set_min_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
- uint16_t version) {
- /* Zero is interpreted as the default minimum version. */
- if (version == 0) {
- *out = method->min_version;
- /* SSL 3.0 is disabled unless explicitly enabled. */
- if (*out < TLS1_VERSION) {
- *out = TLS1_VERSION;
- }
- return 1;
- }
-
- if (version == TLS1_3_VERSION) {
- version = TLS1_3_DRAFT_VERSION;
- }
-
- return method->version_from_wire(out, version);
-}
-
-static int set_max_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
- uint16_t version) {
- /* Zero is interpreted as the default maximum version. */
- if (version == 0) {
- *out = method->max_version;
- /* TODO(svaldez): Enable TLS 1.3 by default once fully implemented. */
- if (*out > TLS1_2_VERSION) {
- *out = TLS1_2_VERSION;
- }
- return 1;
- }
-
- if (version == TLS1_3_VERSION) {
- version = TLS1_3_DRAFT_VERSION;
- }
-
- return method->version_from_wire(out, version);
-}
-
-int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) {
- return set_min_version(ctx->method, &ctx->conf_min_version, version);
-}
-
-int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) {
- return set_max_version(ctx->method, &ctx->conf_max_version, version);
-}
-
-int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
- return set_min_version(ssl->method, &ssl->conf_min_version, version);
-}
-
-int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
- return set_max_version(ssl->method, &ssl->conf_max_version, version);
-}
-
uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
ctx->options |= options;
return ctx->options;
@@ -1901,43 +1847,6 @@ void ssl_update_cache(SSL_HANDSHAKE *hs, int mode) {
}
}
-static const char *ssl_get_version(int version) {
- switch (version) {
- /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
- case TLS1_3_DRAFT_VERSION:
- return "TLSv1.3";
-
- case TLS1_2_VERSION:
- return "TLSv1.2";
-
- case TLS1_1_VERSION:
- return "TLSv1.1";
-
- case TLS1_VERSION:
- return "TLSv1";
-
- case SSL3_VERSION:
- return "SSLv3";
-
- case DTLS1_VERSION:
- return "DTLSv1";
-
- case DTLS1_2_VERSION:
- return "DTLSv1.2";
-
- default:
- return "unknown";
- }
-}
-
-const char *SSL_get_version(const SSL *ssl) {
- return ssl_get_version(ssl->version);
-}
-
-const char *SSL_SESSION_get_version(const SSL_SESSION *session) {
- return ssl_get_version(session->ssl_version);
-}
-
EVP_PKEY *SSL_get_privatekey(const SSL *ssl) {
if (ssl->cert != NULL) {
return ssl->cert->privatekey;
@@ -2015,15 +1924,6 @@ int SSL_get_shutdown(const SSL *ssl) {
return ret;
}
-int SSL_version(const SSL *ssl) {
- /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
- if (ssl->version == TLS1_3_DRAFT_VERSION) {
- return TLS1_3_VERSION;
- }
-
- return ssl->version;
-}
-
SSL_CTX *SSL_get_SSL_CTX(const SSL *ssl) { return ssl->ctx; }
SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx) {
@@ -2329,102 +2229,6 @@ int ssl3_can_false_start(const SSL *ssl) {
cipher->algorithm_mac == SSL_AEAD;
}
-const struct {
- uint16_t version;
- uint32_t flag;
-} kVersions[] = {
- {SSL3_VERSION, SSL_OP_NO_SSLv3},
- {TLS1_VERSION, SSL_OP_NO_TLSv1},
- {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
- {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
- {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
-};
-
-static const size_t kVersionsLen = OPENSSL_ARRAY_SIZE(kVersions);
-
-int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
- uint16_t *out_max_version) {
- /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
- * DTLS 1.0 should be mapped to TLS 1.1. */
- uint32_t options = ssl->options;
- if (SSL_is_dtls(ssl)) {
- options &= ~SSL_OP_NO_TLSv1_1;
- if (options & SSL_OP_NO_DTLSv1) {
- options |= SSL_OP_NO_TLSv1_1;
- }
- }
-
- uint16_t min_version = ssl->conf_min_version;
- uint16_t max_version = ssl->conf_max_version;
-
- /* Bound the range to only those implemented in this protocol. */
- if (min_version < ssl->method->min_version) {
- min_version = ssl->method->min_version;
- }
- if (max_version > ssl->method->max_version) {
- max_version = ssl->method->max_version;
- }
-
- /* OpenSSL's API for controlling versions entails blacklisting individual
- * protocols. This has two problems. First, on the client, the protocol can
- * only express a contiguous range of versions. Second, a library consumer
- * trying to set a maximum version cannot disable protocol versions that get
- * added in a future version of the library.
- *
- * To account for both of these, OpenSSL interprets the client-side bitmask
- * as a min/max range by picking the lowest contiguous non-empty range of
- * enabled protocols. Note that this means it is impossible to set a maximum
- * version of the higest supported TLS version in a future-proof way. */
- int any_enabled = 0;
- for (size_t i = 0; i < kVersionsLen; i++) {
- /* Only look at the versions already enabled. */
- if (min_version > kVersions[i].version) {
- continue;
- }
- if (max_version < kVersions[i].version) {
- break;
- }
-
- if (!(options & kVersions[i].flag)) {
- /* The minimum version is the first enabled version. */
- if (!any_enabled) {
- any_enabled = 1;
- min_version = kVersions[i].version;
- }
- continue;
- }
-
- /* If there is a disabled version after the first enabled one, all versions
- * after it are implicitly disabled. */
- if (any_enabled) {
- max_version = kVersions[i-1].version;
- break;
- }
- }
-
- if (!any_enabled) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SUPPORTED_VERSIONS_ENABLED);
- return 0;
- }
-
- *out_min_version = min_version;
- *out_max_version = max_version;
- return 1;
-}
-
-uint16_t ssl3_protocol_version(const SSL *ssl) {
- assert(ssl->s3->have_version);
- uint16_t version;
- if (!ssl->method->version_from_wire(&version, ssl->version)) {
- /* TODO(davidben): Use the internal version representation for ssl->version
- * and map to the public API representation at API boundaries. */
- assert(0);
- return 0;
- }
-
- return version;
-}
-
int SSL_is_server(const SSL *ssl) { return ssl->server; }
int SSL_is_dtls(const SSL *ssl) { return ssl->method->is_dtls; }
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index f025364b..3e2c9f49 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -505,14 +505,21 @@ void *SSL_SESSION_get_ex_data(const SSL_SESSION *session, int idx) {
return CRYPTO_get_ex_data(&session->ex_data, idx);
}
-const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session,
- const SSL *ssl) {
- uint16_t version;
- if (!ssl->method->version_from_wire(&version, session->ssl_version)) {
- return NULL;
+uint16_t SSL_SESSION_protocol_version(const SSL_SESSION *session) {
+ uint16_t ret;
+ if (!ssl_protocol_version_from_wire(&ret, session->ssl_version)) {
+ /* An |SSL_SESSION| will never have an invalid version. This is enforced by
+ * the parser. */
+ assert(0);
+ return 0;
}
- return ssl_get_handshake_digest(session->cipher->algorithm_prf, version);
+ return ret;
+}
+
+const EVP_MD *SSL_SESSION_get_digest(const SSL_SESSION *session) {
+ return ssl_get_handshake_digest(session->cipher->algorithm_prf,
+ SSL_SESSION_protocol_version(session));
}
int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) {
@@ -952,7 +959,6 @@ static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock) {
}
if (ret) {
- found_session->not_resumable = 1;
if (ctx->remove_session_cb != NULL) {
ctx->remove_session_cb(ctx, found_session);
}
@@ -1031,7 +1037,6 @@ static void timeout_doall_arg(SSL_SESSION *session, void *void_param) {
* save on locking overhead */
(void) lh_SSL_SESSION_delete(param->cache, session);
SSL_SESSION_list_remove(param->ctx, session);
- session->not_resumable = 1;
if (param->ctx->remove_session_cb != NULL) {
param->ctx->remove_session_cb(param->ctx, session);
}
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 84b7496d..2c648acb 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -180,7 +180,7 @@ static const CipherTest kCipherTests[] = {
// Standard names may be used instead of OpenSSL names.
{
"[TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256|"
- "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]:"
+ "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256]:"
"[TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256]:"
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
{
@@ -214,6 +214,32 @@ static const CipherTest kCipherTests[] = {
},
false,
},
+ // Additional masks after @STRENGTH get silently discarded.
+ //
+ // TODO(davidben): Make this an error. If not silently discarded, they get
+ // interpreted as + opcodes which are very different.
+ {
+ "ECDHE-RSA-AES128-GCM-SHA256:"
+ "ECDHE-RSA-AES256-GCM-SHA384:"
+ "@STRENGTH+AES256",
+ {
+ {TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 0},
+ {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+ },
+ false,
+ },
+ {
+ "ECDHE-RSA-AES128-GCM-SHA256:"
+ "ECDHE-RSA-AES256-GCM-SHA384:"
+ "@STRENGTH+AES256:"
+ "ECDHE-RSA-CHACHA20-POLY1305",
+ {
+ {TLS1_CK_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 0},
+ {TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0},
+ {TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0},
+ },
+ false,
+ },
// Exact ciphers may not be used in multi-part rules; they are treated
// as unknown aliases.
{
@@ -2567,6 +2593,10 @@ TEST(SSLTest, SetVersion) {
EXPECT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_VERSION));
EXPECT_EQ(TLS1_3_VERSION, ctx->conf_max_version);
+ // TLS1_3_DRAFT_VERSION is not an API-level version.
+ EXPECT_FALSE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_DRAFT_VERSION));
+ ERR_clear_error();
+
ctx.reset(SSL_CTX_new(DTLS_method()));
ASSERT_TRUE(ctx);
diff --git a/src/ssl/ssl_versions.c b/src/ssl/ssl_versions.c
new file mode 100644
index 00000000..5d927718
--- /dev/null
+++ b/src/ssl/ssl_versions.c
@@ -0,0 +1,343 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
+
+#include "internal.h"
+#include "../crypto/internal.h"
+
+
+int ssl_protocol_version_from_wire(uint16_t *out, uint16_t version) {
+ switch (version) {
+ case SSL3_VERSION:
+ case TLS1_VERSION:
+ case TLS1_1_VERSION:
+ case TLS1_2_VERSION:
+ *out = version;
+ return 1;
+
+ case TLS1_3_DRAFT_VERSION:
+ *out = TLS1_3_VERSION;
+ return 1;
+
+ case DTLS1_VERSION:
+ /* DTLS 1.0 is analogous to TLS 1.1, not TLS 1.0. */
+ *out = TLS1_1_VERSION;
+ return 1;
+
+ case DTLS1_2_VERSION:
+ *out = TLS1_2_VERSION;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* The follow arrays are the supported versions for TLS and DTLS, in order of
+ * decreasing preference. */
+
+static const uint16_t kTLSVersions[] = {
+ TLS1_3_DRAFT_VERSION,
+ TLS1_2_VERSION,
+ TLS1_1_VERSION,
+ TLS1_VERSION,
+ SSL3_VERSION,
+};
+
+static const uint16_t kDTLSVersions[] = {
+ DTLS1_2_VERSION,
+ DTLS1_VERSION,
+};
+
+static void get_method_versions(const SSL_PROTOCOL_METHOD *method,
+ const uint16_t **out, size_t *out_num) {
+ if (method->is_dtls) {
+ *out = kDTLSVersions;
+ *out_num = OPENSSL_ARRAY_SIZE(kDTLSVersions);
+ } else {
+ *out = kTLSVersions;
+ *out_num = OPENSSL_ARRAY_SIZE(kTLSVersions);
+ }
+}
+
+static int method_supports_version(const SSL_PROTOCOL_METHOD *method,
+ uint16_t version) {
+ const uint16_t *versions;
+ size_t num_versions;
+ get_method_versions(method, &versions, &num_versions);
+ for (size_t i = 0; i < num_versions; i++) {
+ if (versions[i] == version) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int set_version_bound(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+ uint16_t version) {
+ /* The public API uses wire versions, except we use |TLS1_3_VERSION|
+ * everywhere to refer to any draft TLS 1.3 versions. In this direction, we
+ * map it to some representative TLS 1.3 draft version. */
+ if (version == TLS1_3_DRAFT_VERSION) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION);
+ return 0;
+ }
+ if (version == TLS1_3_VERSION) {
+ version = TLS1_3_DRAFT_VERSION;
+ }
+
+ if (!method_supports_version(method, version) ||
+ !ssl_protocol_version_from_wire(out, version)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_SSL_VERSION);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int set_min_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+ uint16_t version) {
+ /* Zero is interpreted as the default minimum version. */
+ if (version == 0) {
+ /* SSL 3.0 is disabled by default and TLS 1.0 does not exist in DTLS. */
+ *out = method->is_dtls ? TLS1_1_VERSION : TLS1_VERSION;
+ return 1;
+ }
+
+ return set_version_bound(method, out, version);
+}
+
+static int set_max_version(const SSL_PROTOCOL_METHOD *method, uint16_t *out,
+ uint16_t version) {
+ /* Zero is interpreted as the default maximum version. */
+ if (version == 0) {
+ *out = TLS1_2_VERSION;
+ return 1;
+ }
+
+ return set_version_bound(method, out, version);
+}
+
+int SSL_CTX_set_min_proto_version(SSL_CTX *ctx, uint16_t version) {
+ return set_min_version(ctx->method, &ctx->conf_min_version, version);
+}
+
+int SSL_CTX_set_max_proto_version(SSL_CTX *ctx, uint16_t version) {
+ return set_max_version(ctx->method, &ctx->conf_max_version, version);
+}
+
+int SSL_set_min_proto_version(SSL *ssl, uint16_t version) {
+ return set_min_version(ssl->method, &ssl->conf_min_version, version);
+}
+
+int SSL_set_max_proto_version(SSL *ssl, uint16_t version) {
+ return set_max_version(ssl->method, &ssl->conf_max_version, version);
+}
+
+const struct {
+ uint16_t version;
+ uint32_t flag;
+} kProtocolVersions[] = {
+ {SSL3_VERSION, SSL_OP_NO_SSLv3},
+ {TLS1_VERSION, SSL_OP_NO_TLSv1},
+ {TLS1_1_VERSION, SSL_OP_NO_TLSv1_1},
+ {TLS1_2_VERSION, SSL_OP_NO_TLSv1_2},
+ {TLS1_3_VERSION, SSL_OP_NO_TLSv1_3},
+};
+
+int ssl_get_version_range(const SSL *ssl, uint16_t *out_min_version,
+ uint16_t *out_max_version) {
+ /* For historical reasons, |SSL_OP_NO_DTLSv1| aliases |SSL_OP_NO_TLSv1|, but
+ * DTLS 1.0 should be mapped to TLS 1.1. */
+ uint32_t options = ssl->options;
+ if (SSL_is_dtls(ssl)) {
+ options &= ~SSL_OP_NO_TLSv1_1;
+ if (options & SSL_OP_NO_DTLSv1) {
+ options |= SSL_OP_NO_TLSv1_1;
+ }
+ }
+
+ uint16_t min_version = ssl->conf_min_version;
+ uint16_t max_version = ssl->conf_max_version;
+
+ /* OpenSSL's API for controlling versions entails blacklisting individual
+ * protocols. This has two problems. First, on the client, the protocol can
+ * only express a contiguous range of versions. Second, a library consumer
+ * trying to set a maximum version cannot disable protocol versions that get
+ * added in a future version of the library.
+ *
+ * To account for both of these, OpenSSL interprets the client-side bitmask
+ * as a min/max range by picking the lowest contiguous non-empty range of
+ * enabled protocols. Note that this means it is impossible to set a maximum
+ * version of the higest supported TLS version in a future-proof way. */
+ int any_enabled = 0;
+ for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kProtocolVersions); i++) {
+ /* Only look at the versions already enabled. */
+ if (min_version > kProtocolVersions[i].version) {
+ continue;
+ }
+ if (max_version < kProtocolVersions[i].version) {
+ break;
+ }
+
+ if (!(options & kProtocolVersions[i].flag)) {
+ /* The minimum version is the first enabled version. */
+ if (!any_enabled) {
+ any_enabled = 1;
+ min_version = kProtocolVersions[i].version;
+ }
+ continue;
+ }
+
+ /* If there is a disabled version after the first enabled one, all versions
+ * after it are implicitly disabled. */
+ if (any_enabled) {
+ max_version = kProtocolVersions[i-1].version;
+ break;
+ }
+ }
+
+ if (!any_enabled) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SUPPORTED_VERSIONS_ENABLED);
+ return 0;
+ }
+
+ *out_min_version = min_version;
+ *out_max_version = max_version;
+ return 1;
+}
+
+static uint16_t ssl_version(const SSL *ssl) {
+ /* In early data, we report the predicted version. */
+ if (SSL_in_early_data(ssl) && !ssl->server) {
+ return ssl->s3->hs->early_session->ssl_version;
+ }
+ return ssl->version;
+}
+
+int SSL_version(const SSL *ssl) {
+ uint16_t ret = ssl_version(ssl);
+ /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
+ if (ret == TLS1_3_DRAFT_VERSION) {
+ return TLS1_3_VERSION;
+ }
+ return ret;
+}
+
+static const char *ssl_get_version(int version) {
+ switch (version) {
+ /* Report TLS 1.3 draft version as TLS 1.3 in the public API. */
+ case TLS1_3_DRAFT_VERSION:
+ return "TLSv1.3";
+
+ case TLS1_2_VERSION:
+ return "TLSv1.2";
+
+ case TLS1_1_VERSION:
+ return "TLSv1.1";
+
+ case TLS1_VERSION:
+ return "TLSv1";
+
+ case SSL3_VERSION:
+ return "SSLv3";
+
+ case DTLS1_VERSION:
+ return "DTLSv1";
+
+ case DTLS1_2_VERSION:
+ return "DTLSv1.2";
+
+ default:
+ return "unknown";
+ }
+}
+
+const char *SSL_get_version(const SSL *ssl) {
+ return ssl_get_version(ssl_version(ssl));
+}
+
+const char *SSL_SESSION_get_version(const SSL_SESSION *session) {
+ return ssl_get_version(session->ssl_version);
+}
+
+uint16_t ssl3_protocol_version(const SSL *ssl) {
+ assert(ssl->s3->have_version);
+ uint16_t version;
+ if (!ssl_protocol_version_from_wire(&version, ssl->version)) {
+ /* |ssl->version| will always be set to a valid version. */
+ assert(0);
+ return 0;
+ }
+
+ return version;
+}
+
+int ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) {
+ uint16_t protocol_version;
+ return method_supports_version(hs->ssl->method, version) &&
+ ssl_protocol_version_from_wire(&protocol_version, version) &&
+ hs->min_version <= protocol_version &&
+ protocol_version <= hs->max_version;
+}
+
+int ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb) {
+ const uint16_t *versions;
+ size_t num_versions;
+ get_method_versions(hs->ssl->method, &versions, &num_versions);
+ for (size_t i = 0; i < num_versions; i++) {
+ if (ssl_supports_version(hs, versions[i]) &&
+ !CBB_add_u16(cbb, versions[i])) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int ssl_negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+ uint16_t *out_version, const CBS *peer_versions) {
+ const uint16_t *versions;
+ size_t num_versions;
+ get_method_versions(hs->ssl->method, &versions, &num_versions);
+ for (size_t i = 0; i < num_versions; i++) {
+ if (!ssl_supports_version(hs, versions[i])) {
+ continue;
+ }
+
+ CBS copy = *peer_versions;
+ while (CBS_len(&copy) != 0) {
+ uint16_t version;
+ if (!CBS_get_u16(&copy, &version)) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ if (version == versions[i]) {
+ *out_version = version;
+ return 1;
+ }
+ }
+ }
+
+ OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_PROTOCOL);
+ *out_alert = SSL_AD_PROTOCOL_VERSION;
+ return 0;
+}
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 1b143711..8e858c42 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -799,16 +799,24 @@ static int ext_ri_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
}
const uint8_t *d = CBS_data(&renegotiated_connection);
- if (CRYPTO_memcmp(d, ssl->s3->previous_client_finished,
- ssl->s3->previous_client_finished_len)) {
+ int ok = CRYPTO_memcmp(d, ssl->s3->previous_client_finished,
+ ssl->s3->previous_client_finished_len) == 0;
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+ ok = 1;
+#endif
+ if (!ok) {
OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
*out_alert = SSL_AD_HANDSHAKE_FAILURE;
return 0;
}
d += ssl->s3->previous_client_finished_len;
- if (CRYPTO_memcmp(d, ssl->s3->previous_server_finished,
- ssl->s3->previous_server_finished_len)) {
+ ok = CRYPTO_memcmp(d, ssl->s3->previous_server_finished,
+ ssl->s3->previous_server_finished_len) == 0;
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+ ok = 1;
+#endif
+ if (!ok) {
OPENSSL_PUT_ERROR(SSL, SSL_R_RENEGOTIATION_MISMATCH);
*out_alert = SSL_AD_ILLEGAL_PARAMETER;
return 0;
@@ -970,14 +978,11 @@ static int ext_ticket_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
* advertise the extension to avoid potentially breaking servers which carry
* over the state from the previous handshake, such as OpenSSL servers
* without upstream's 3c3f0259238594d77264a78944d409f2127642c4. */
- uint16_t session_version;
if (!ssl->s3->initial_handshake_complete &&
ssl->session != NULL &&
ssl->session->tlsext_tick != NULL &&
/* Don't send TLS 1.3 session tickets in the ticket extension. */
- ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) &&
- session_version < TLS1_3_VERSION) {
+ SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
ticket_data = ssl->session->tlsext_tick;
ticket_len = ssl->session->tlsext_ticklen;
}
@@ -1863,31 +1868,19 @@ static int ext_ec_point_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- uint16_t session_version;
if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
- !ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- session_version < TLS1_3_VERSION) {
+ SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
return 0;
}
- const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
- if (digest == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
- size_t binder_len = EVP_MD_size(digest);
+ size_t binder_len = EVP_MD_size(SSL_SESSION_get_digest(ssl->session));
return 15 + ssl->session->tlsext_ticklen + binder_len;
}
static int ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- uint16_t session_version;
if (hs->max_version < TLS1_3_VERSION || ssl->session == NULL ||
- !ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- session_version < TLS1_3_VERSION) {
+ SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION) {
return 1;
}
@@ -1899,14 +1892,7 @@ static int ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
/* Fill in a placeholder zero binder of the appropriate length. It will be
* computed and filled in later after length prefixes are computed. */
uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
-
- const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
- if (digest == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
-
- size_t binder_len = EVP_MD_size(digest);
+ size_t binder_len = EVP_MD_size(SSL_SESSION_get_digest(ssl->session));
CBB contents, identity, ticket, binders, binder;
if (!CBB_add_u16(out, TLSEXT_TYPE_pre_shared_key) ||
@@ -2071,11 +2057,8 @@ static int ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs,
static int ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- uint16_t session_version;
if (ssl->session == NULL ||
- !ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- session_version < TLS1_3_VERSION ||
+ SSL_SESSION_protocol_version(ssl->session) < TLS1_3_VERSION ||
ssl->session->ticket_max_early_data == 0 ||
hs->received_hello_retry_request ||
!ssl->cert->enable_early_data) {
@@ -2375,14 +2358,8 @@ static int ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
return 0;
}
- for (uint16_t version = hs->max_version; version >= hs->min_version;
- version--) {
- if (!CBB_add_u16(&versions, ssl->method->version_to_wire(version))) {
- return 0;
- }
- }
-
- if (!CBB_flush(out)) {
+ if (!ssl_add_supported_versions(hs, &versions) ||
+ !CBB_flush(out)) {
return 0;
}
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 3cb2856d..6d3e6d1c 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -62,6 +62,7 @@ OPENSSL_MSVC_PRAGMA(comment(lib, "Ws2_32.lib"))
#include "../../crypto/internal.h"
#include "../internal.h"
#include "async_bio.h"
+#include "fuzzer.h"
#include "packeted_bio.h"
#include "test_config.h"
@@ -1371,6 +1372,13 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume,
return false;
}
+ if (config->expect_version != 0 &&
+ SSL_version(ssl) != config->expect_version) {
+ fprintf(stderr, "want version %04x, got %04x\n", config->expect_version,
+ SSL_version(ssl));
+ return false;
+ }
+
bool expect_resume =
is_resume && (!config->expect_session_miss || SSL_in_early_data(ssl));
if (!!SSL_session_reused(ssl) != expect_resume) {
@@ -1645,6 +1653,61 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume,
return true;
}
+static bool WriteSettings(int i, const TestConfig *config,
+ const SSL_SESSION *session) {
+ if (config->write_settings.empty()) {
+ return true;
+ }
+
+ // Treat write_settings as a path prefix for each connection in the run.
+ char buf[DECIMAL_SIZE(int)];
+ snprintf(buf, sizeof(buf), "%d", i);
+ std::string path = config->write_settings + buf;
+
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), 64)) {
+ return false;
+ }
+
+ if (session != nullptr) {
+ uint8_t *data;
+ size_t len;
+ if (!SSL_SESSION_to_bytes(session, &data, &len)) {
+ return false;
+ }
+ bssl::UniquePtr<uint8_t> free_data(data);
+ CBB child;
+ if (!CBB_add_u16(cbb.get(), kSessionTag) ||
+ !CBB_add_u24_length_prefixed(cbb.get(), &child) ||
+ !CBB_add_bytes(&child, data, len) ||
+ !CBB_flush(cbb.get())) {
+ return false;
+ }
+ }
+
+ if (config->is_server &&
+ (config->require_any_client_certificate || config->verify_peer) &&
+ !CBB_add_u16(cbb.get(), kRequestClientCert)) {
+ return false;
+ }
+
+ uint8_t *settings;
+ size_t settings_len;
+ if (!CBB_add_u16(cbb.get(), kDataTag) ||
+ !CBB_finish(cbb.get(), &settings, &settings_len)) {
+ return false;
+ }
+ bssl::UniquePtr<uint8_t> free_settings(settings);
+
+ using ScopedFILE = std::unique_ptr<FILE, decltype(&fclose)>;
+ ScopedFILE file(fopen(path.c_str(), "w"), fclose);
+ if (!file) {
+ return false;
+ }
+
+ return fwrite(settings, settings_len, 1, file.get()) == 1;
+}
+
static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, SSL *ssl,
const TestConfig *config, bool is_resume, bool is_retry);
@@ -1683,12 +1746,20 @@ static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session,
SSL_set_cert_cb(ssl.get(), CertCallback, nullptr);
}
if (config->require_any_client_certificate) {
- SSL_set_verify(ssl.get(), SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ SSL_set_verify(ssl.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
NULL);
}
if (config->verify_peer) {
SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, NULL);
}
+ if (config->verify_peer_if_no_obc) {
+ // Set SSL_VERIFY_FAIL_IF_NO_PEER_CERT so testing whether client
+ // certificates were requested is easy.
+ SSL_set_verify(ssl.get(),
+ SSL_VERIFY_PEER | SSL_VERIFY_PEER_IF_NO_OBC |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
+ NULL);
+ }
if (config->false_start) {
SSL_set_mode(ssl.get(), SSL_MODE_ENABLE_FALSE_START);
}
@@ -2227,6 +2298,10 @@ int main(int argc, char **argv) {
}
bssl::UniquePtr<SSL_SESSION> offer_session = std::move(session);
+ if (!WriteSettings(i, config, offer_session.get())) {
+ fprintf(stderr, "Error writing settings.\n");
+ return 1;
+ }
if (!DoConnection(&session, ssl_ctx.get(), config, &retry_config, is_resume,
offer_session.get())) {
fprintf(stderr, "Connection %d failed.\n", i + 1);
diff --git a/src/ssl/test/fuzzer.h b/src/ssl/test/fuzzer.h
new file mode 100644
index 00000000..2f816534
--- /dev/null
+++ b/src/ssl/test/fuzzer.h
@@ -0,0 +1,271 @@
+/* Copyright (c) 2017, 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_SSL_TEST_FUZZER
+#define HEADER_SSL_TEST_FUZZER
+
+#include <openssl/bytestring.h>
+#include <openssl/ssl.h>
+
+
+// SSL fuzzer utilities.
+//
+// The TLS client and server fuzzers coordinate with bssl_shim on a common
+// format to encode configuration parameters in a fuzzer file. To add a new
+// configuration, define a tag, update |SetupTest| below to parse it, and
+// update |WriteSettings| in bssl_shim to serialize it. Finally, record
+// transcripts from a test run, and use the BORINGSSL_FUZZER_DEBUG environment
+// variable to confirm the transcripts are compatible.
+
+// kDataTag denotes that the remainder of the input should be passed to the TLS
+// stack.
+static const uint16_t kDataTag = 0;
+
+// kSessionTag is followed by a u24-length-prefixed serialized SSL_SESSION to
+// resume.
+static const uint16_t kSessionTag = 1;
+
+// kRequestClientCert denotes that the server should request client
+// certificates.
+static const uint16_t kRequestClientCert = 2;
+
+// SetupTest parses parameters from |cbs| and returns a newly-configured |SSL|
+// object or nullptr on error. On success, the caller should feed the remaining
+// input in |cbs| to the SSL stack.
+static inline bssl::UniquePtr<SSL> SetupTest(CBS *cbs, SSL_CTX *ctx,
+ bool is_server) {
+ // Clear any sessions saved in |ctx| from the previous run.
+ SSL_CTX_flush_sessions(ctx, 0);
+
+ bssl::UniquePtr<SSL> ssl(SSL_new(ctx));
+ if (is_server) {
+ SSL_set_accept_state(ssl.get());
+ } else {
+ SSL_set_connect_state(ssl.get());
+ }
+
+ for (;;) {
+ uint16_t tag;
+ if (!CBS_get_u16(cbs, &tag)) {
+ return nullptr;
+ }
+ switch (tag) {
+ case kDataTag:
+ return ssl;
+
+ case kSessionTag: {
+ CBS data;
+ if (!CBS_get_u24_length_prefixed(cbs, &data)) {
+ return nullptr;
+ }
+ bssl::UniquePtr<SSL_SESSION> session(
+ SSL_SESSION_from_bytes(CBS_data(&data), CBS_len(&data), ctx));
+ if (!session) {
+ return nullptr;
+ }
+
+ if (is_server) {
+ SSL_CTX_add_session(ctx, session.get());
+ } else {
+ SSL_set_session(ssl.get(), session.get());
+ }
+ break;
+ }
+
+ case kRequestClientCert:
+ if (!is_server) {
+ return nullptr;
+ }
+ SSL_set_verify(ssl.get(), SSL_VERIFY_PEER, nullptr);
+ break;
+ }
+ }
+}
+
+
+// Additional shared constants.
+
+static const uint8_t kCertificateDER[] = {
+ 0x30, 0x82, 0x02, 0xff, 0x30, 0x82, 0x01, 0xe7, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x11, 0x00, 0xb1, 0x84, 0xee, 0x34, 0x99, 0x98, 0x76, 0xfb,
+ 0x6f, 0xb2, 0x15, 0xc8, 0x47, 0x79, 0x05, 0x9b, 0x30, 0x0d, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30,
+ 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07,
+ 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x35, 0x31, 0x31, 0x30, 0x37, 0x30, 0x30, 0x32, 0x34, 0x35, 0x36, 0x5a,
+ 0x17, 0x0d, 0x31, 0x36, 0x31, 0x31, 0x30, 0x36, 0x30, 0x30, 0x32, 0x34,
+ 0x35, 0x36, 0x5a, 0x30, 0x12, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55,
+ 0x04, 0x0a, 0x13, 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, 0x30,
+ 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30,
+ 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce, 0x47, 0xcb, 0x11,
+ 0xbb, 0xd2, 0x9d, 0x8e, 0x9e, 0xd2, 0x1e, 0x14, 0xaf, 0xc7, 0xea, 0xb6,
+ 0xc9, 0x38, 0x2a, 0x6f, 0xb3, 0x7e, 0xfb, 0xbc, 0xfc, 0x59, 0x42, 0xb9,
+ 0x56, 0xf0, 0x4c, 0x3f, 0xf7, 0x31, 0x84, 0xbe, 0xac, 0x03, 0x9e, 0x71,
+ 0x91, 0x85, 0xd8, 0x32, 0xbd, 0x00, 0xea, 0xac, 0x65, 0xf6, 0x03, 0xc8,
+ 0x0f, 0x8b, 0xfd, 0x6e, 0x58, 0x88, 0x04, 0x41, 0x92, 0x74, 0xa6, 0x57,
+ 0x2e, 0x8e, 0x88, 0xd5, 0x3d, 0xda, 0x14, 0x3e, 0x63, 0x88, 0x22, 0xe3,
+ 0x53, 0xe9, 0xba, 0x39, 0x09, 0xac, 0xfb, 0xd0, 0x4c, 0xf2, 0x3c, 0x20,
+ 0xd6, 0x97, 0xe6, 0xed, 0xf1, 0x62, 0x1e, 0xe5, 0xc9, 0x48, 0xa0, 0xca,
+ 0x2e, 0x3c, 0x14, 0x5a, 0x82, 0xd4, 0xed, 0xb1, 0xe3, 0x43, 0xc1, 0x2a,
+ 0x59, 0xa5, 0xb9, 0xc8, 0x48, 0xa7, 0x39, 0x23, 0x74, 0xa7, 0x37, 0xb0,
+ 0x6f, 0xc3, 0x64, 0x99, 0x6c, 0xa2, 0x82, 0xc8, 0xf6, 0xdb, 0x86, 0x40,
+ 0xce, 0xd1, 0x85, 0x9f, 0xce, 0x69, 0xf4, 0x15, 0x2a, 0x23, 0xca, 0xea,
+ 0xb7, 0x7b, 0xdf, 0xfb, 0x43, 0x5f, 0xff, 0x7a, 0x49, 0x49, 0x0e, 0xe7,
+ 0x02, 0x51, 0x45, 0x13, 0xe8, 0x90, 0x64, 0x21, 0x0c, 0x26, 0x2b, 0x5d,
+ 0xfc, 0xe4, 0xb5, 0x86, 0x89, 0x43, 0x22, 0x4c, 0xf3, 0x3b, 0xf3, 0x09,
+ 0xc4, 0xa4, 0x10, 0x80, 0xf2, 0x46, 0xe2, 0x46, 0x8f, 0x76, 0x50, 0xbf,
+ 0xaf, 0x2b, 0x90, 0x1b, 0x78, 0xc7, 0xcf, 0xc1, 0x77, 0xd0, 0xfb, 0xa9,
+ 0xfb, 0xc9, 0x66, 0x5a, 0xc5, 0x9b, 0x31, 0x41, 0x67, 0x01, 0xbe, 0x33,
+ 0x10, 0xba, 0x05, 0x58, 0xed, 0x76, 0x53, 0xde, 0x5d, 0xc1, 0xe8, 0xbb,
+ 0x9f, 0xf1, 0xcd, 0xfb, 0xdf, 0x64, 0x7f, 0xd7, 0x18, 0xab, 0x0f, 0x94,
+ 0x28, 0x95, 0x4a, 0xcc, 0x6a, 0xa9, 0x50, 0xc7, 0x05, 0x47, 0x10, 0x41,
+ 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x0e, 0x06,
+ 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05,
+ 0xa0, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x0c, 0x30, 0x0a,
+ 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+ 0x30, 0x19, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x12, 0x30, 0x10, 0x82,
+ 0x0e, 0x66, 0x75, 0x7a, 0x7a, 0x2e, 0x62, 0x6f, 0x72, 0x69, 0x6e, 0x67,
+ 0x73, 0x73, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x92,
+ 0xde, 0xef, 0x96, 0x06, 0x7b, 0xff, 0x71, 0x7d, 0x4e, 0xa0, 0x7d, 0xae,
+ 0xb8, 0x22, 0xb4, 0x2c, 0xf7, 0x96, 0x9c, 0x37, 0x1d, 0x8f, 0xe7, 0xd9,
+ 0x47, 0xff, 0x3f, 0xe9, 0x35, 0x95, 0x0e, 0xdd, 0xdc, 0x7f, 0xc8, 0x8a,
+ 0x1e, 0x36, 0x1d, 0x38, 0x47, 0xfc, 0x76, 0xd2, 0x1f, 0x98, 0xa1, 0x36,
+ 0xac, 0xc8, 0x70, 0x38, 0x0a, 0x3d, 0x51, 0x8d, 0x0f, 0x03, 0x1b, 0xef,
+ 0x62, 0xa1, 0xcb, 0x2b, 0x4a, 0x8c, 0x12, 0x2b, 0x54, 0x50, 0x9a, 0x6b,
+ 0xfe, 0xaf, 0xd9, 0xf6, 0xbf, 0x58, 0x11, 0x58, 0x5e, 0xe5, 0x86, 0x1e,
+ 0x3b, 0x6b, 0x30, 0x7e, 0x72, 0x89, 0xe8, 0x6b, 0x7b, 0xb7, 0xaf, 0xef,
+ 0x8b, 0xa9, 0x3e, 0xb0, 0xcd, 0x0b, 0xef, 0xb0, 0x0c, 0x96, 0x2b, 0xc5,
+ 0x3b, 0xd5, 0xf1, 0xc2, 0xae, 0x3a, 0x60, 0xd9, 0x0f, 0x75, 0x37, 0x55,
+ 0x4d, 0x62, 0xd2, 0xed, 0x96, 0xac, 0x30, 0x6b, 0xda, 0xa1, 0x48, 0x17,
+ 0x96, 0x23, 0x85, 0x9a, 0x57, 0x77, 0xe9, 0x22, 0xa2, 0x37, 0x03, 0xba,
+ 0x49, 0x77, 0x40, 0x3b, 0x76, 0x4b, 0xda, 0xc1, 0x04, 0x57, 0x55, 0x34,
+ 0x22, 0x83, 0x45, 0x29, 0xab, 0x2e, 0x11, 0xff, 0x0d, 0xab, 0x55, 0xb1,
+ 0xa7, 0x58, 0x59, 0x05, 0x25, 0xf9, 0x1e, 0x3d, 0xb7, 0xac, 0x04, 0x39,
+ 0x2c, 0xf9, 0xaf, 0xb8, 0x68, 0xfb, 0x8e, 0x35, 0x71, 0x32, 0xff, 0x70,
+ 0xe9, 0x46, 0x6d, 0x5c, 0x06, 0x90, 0x88, 0x23, 0x48, 0x0c, 0x50, 0xeb,
+ 0x0a, 0xa9, 0xae, 0xe8, 0xfc, 0xbe, 0xa5, 0x76, 0x94, 0xd7, 0x64, 0x22,
+ 0x38, 0x98, 0x17, 0xa4, 0x3a, 0xa7, 0x59, 0x9f, 0x1d, 0x3b, 0x75, 0x90,
+ 0x1a, 0x81, 0xef, 0x19, 0xfb, 0x2b, 0xb7, 0xa7, 0x64, 0x61, 0x22, 0xa4,
+ 0x6f, 0x7b, 0xfa, 0x58, 0xbb, 0x8c, 0x4e, 0x77, 0x67, 0xd0, 0x5d, 0x58,
+ 0x76, 0x8a, 0xbb,
+};
+
+static const uint8_t kRSAPrivateKeyDER[] = {
+ 0x30, 0x82, 0x04, 0xa5, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00,
+ 0xce, 0x47, 0xcb, 0x11, 0xbb, 0xd2, 0x9d, 0x8e, 0x9e, 0xd2, 0x1e, 0x14,
+ 0xaf, 0xc7, 0xea, 0xb6, 0xc9, 0x38, 0x2a, 0x6f, 0xb3, 0x7e, 0xfb, 0xbc,
+ 0xfc, 0x59, 0x42, 0xb9, 0x56, 0xf0, 0x4c, 0x3f, 0xf7, 0x31, 0x84, 0xbe,
+ 0xac, 0x03, 0x9e, 0x71, 0x91, 0x85, 0xd8, 0x32, 0xbd, 0x00, 0xea, 0xac,
+ 0x65, 0xf6, 0x03, 0xc8, 0x0f, 0x8b, 0xfd, 0x6e, 0x58, 0x88, 0x04, 0x41,
+ 0x92, 0x74, 0xa6, 0x57, 0x2e, 0x8e, 0x88, 0xd5, 0x3d, 0xda, 0x14, 0x3e,
+ 0x63, 0x88, 0x22, 0xe3, 0x53, 0xe9, 0xba, 0x39, 0x09, 0xac, 0xfb, 0xd0,
+ 0x4c, 0xf2, 0x3c, 0x20, 0xd6, 0x97, 0xe6, 0xed, 0xf1, 0x62, 0x1e, 0xe5,
+ 0xc9, 0x48, 0xa0, 0xca, 0x2e, 0x3c, 0x14, 0x5a, 0x82, 0xd4, 0xed, 0xb1,
+ 0xe3, 0x43, 0xc1, 0x2a, 0x59, 0xa5, 0xb9, 0xc8, 0x48, 0xa7, 0x39, 0x23,
+ 0x74, 0xa7, 0x37, 0xb0, 0x6f, 0xc3, 0x64, 0x99, 0x6c, 0xa2, 0x82, 0xc8,
+ 0xf6, 0xdb, 0x86, 0x40, 0xce, 0xd1, 0x85, 0x9f, 0xce, 0x69, 0xf4, 0x15,
+ 0x2a, 0x23, 0xca, 0xea, 0xb7, 0x7b, 0xdf, 0xfb, 0x43, 0x5f, 0xff, 0x7a,
+ 0x49, 0x49, 0x0e, 0xe7, 0x02, 0x51, 0x45, 0x13, 0xe8, 0x90, 0x64, 0x21,
+ 0x0c, 0x26, 0x2b, 0x5d, 0xfc, 0xe4, 0xb5, 0x86, 0x89, 0x43, 0x22, 0x4c,
+ 0xf3, 0x3b, 0xf3, 0x09, 0xc4, 0xa4, 0x10, 0x80, 0xf2, 0x46, 0xe2, 0x46,
+ 0x8f, 0x76, 0x50, 0xbf, 0xaf, 0x2b, 0x90, 0x1b, 0x78, 0xc7, 0xcf, 0xc1,
+ 0x77, 0xd0, 0xfb, 0xa9, 0xfb, 0xc9, 0x66, 0x5a, 0xc5, 0x9b, 0x31, 0x41,
+ 0x67, 0x01, 0xbe, 0x33, 0x10, 0xba, 0x05, 0x58, 0xed, 0x76, 0x53, 0xde,
+ 0x5d, 0xc1, 0xe8, 0xbb, 0x9f, 0xf1, 0xcd, 0xfb, 0xdf, 0x64, 0x7f, 0xd7,
+ 0x18, 0xab, 0x0f, 0x94, 0x28, 0x95, 0x4a, 0xcc, 0x6a, 0xa9, 0x50, 0xc7,
+ 0x05, 0x47, 0x10, 0x41, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01,
+ 0x01, 0x00, 0xa8, 0x47, 0xb9, 0x4a, 0x06, 0x47, 0x93, 0x71, 0x3d, 0xef,
+ 0x7b, 0xca, 0xb4, 0x7c, 0x0a, 0xe6, 0x82, 0xd0, 0xe7, 0x0d, 0xa9, 0x08,
+ 0xf6, 0xa4, 0xfd, 0xd8, 0x73, 0xae, 0x6f, 0x56, 0x29, 0x5e, 0x25, 0x72,
+ 0xa8, 0x30, 0x44, 0x73, 0xcf, 0x56, 0x26, 0xb9, 0x61, 0xde, 0x42, 0x81,
+ 0xf4, 0xf0, 0x1f, 0x5d, 0xcb, 0x47, 0xf2, 0x26, 0xe9, 0xe0, 0x93, 0x28,
+ 0xa3, 0x10, 0x3b, 0x42, 0x1e, 0x51, 0x11, 0x12, 0x06, 0x5e, 0xaf, 0xce,
+ 0xb0, 0xa5, 0x14, 0xdd, 0x82, 0x58, 0xa1, 0xa4, 0x12, 0xdf, 0x65, 0x1d,
+ 0x51, 0x70, 0x64, 0xd5, 0x58, 0x68, 0x11, 0xa8, 0x6a, 0x23, 0xc2, 0xbf,
+ 0xa1, 0x25, 0x24, 0x47, 0xb3, 0xa4, 0x3c, 0x83, 0x96, 0xb7, 0x1f, 0xf4,
+ 0x44, 0xd4, 0xd1, 0xe9, 0xfc, 0x33, 0x68, 0x5e, 0xe2, 0x68, 0x99, 0x9c,
+ 0x91, 0xe8, 0x72, 0xc9, 0xd7, 0x8c, 0x80, 0x20, 0x8e, 0x77, 0x83, 0x4d,
+ 0xe4, 0xab, 0xf9, 0x74, 0xa1, 0xdf, 0xd3, 0xc0, 0x0d, 0x5b, 0x05, 0x51,
+ 0xc2, 0x6f, 0xb2, 0x91, 0x02, 0xec, 0xc0, 0x02, 0x1a, 0x5c, 0x91, 0x05,
+ 0xf1, 0xe3, 0xfa, 0x65, 0xc2, 0xad, 0x24, 0xe6, 0xe5, 0x3c, 0xb6, 0x16,
+ 0xf1, 0xa1, 0x67, 0x1a, 0x9d, 0x37, 0x56, 0xbf, 0x01, 0xd7, 0x3b, 0x35,
+ 0x30, 0x57, 0x73, 0xf4, 0xf0, 0x5e, 0xa7, 0xe8, 0x0a, 0xc1, 0x94, 0x17,
+ 0xcf, 0x0a, 0xbd, 0xf5, 0x31, 0xa7, 0x2d, 0xf7, 0xf5, 0xd9, 0x8c, 0xc2,
+ 0x01, 0xbd, 0xda, 0x16, 0x8e, 0xb9, 0x30, 0x40, 0xa6, 0x6e, 0xbd, 0xcd,
+ 0x4d, 0x84, 0x67, 0x4e, 0x0b, 0xce, 0xd5, 0xef, 0xf8, 0x08, 0x63, 0x02,
+ 0xc6, 0xc7, 0xf7, 0x67, 0x92, 0xe2, 0x23, 0x9d, 0x27, 0x22, 0x1d, 0xc6,
+ 0x67, 0x5e, 0x66, 0xbf, 0x03, 0xb8, 0xa9, 0x67, 0xd4, 0x39, 0xd8, 0x75,
+ 0xfa, 0xe8, 0xed, 0x56, 0xb8, 0x81, 0x02, 0x81, 0x81, 0x00, 0xf7, 0x46,
+ 0x68, 0xc6, 0x13, 0xf8, 0xba, 0x0f, 0x83, 0xdb, 0x05, 0xa8, 0x25, 0x00,
+ 0x70, 0x9c, 0x9e, 0x8b, 0x12, 0x34, 0x0d, 0x96, 0xcf, 0x0d, 0x98, 0x9b,
+ 0x8d, 0x9c, 0x96, 0x78, 0xd1, 0x3c, 0x01, 0x8c, 0xb9, 0x35, 0x5c, 0x20,
+ 0x42, 0xb4, 0x38, 0xe3, 0xd6, 0x54, 0xe7, 0x55, 0xd6, 0x26, 0x8a, 0x0c,
+ 0xf6, 0x1f, 0xe0, 0x04, 0xc1, 0x22, 0x42, 0x19, 0x61, 0xc4, 0x94, 0x7c,
+ 0x07, 0x2e, 0x80, 0x52, 0xfe, 0x8d, 0xe6, 0x92, 0x3a, 0x91, 0xfe, 0x72,
+ 0x99, 0xe1, 0x2a, 0x73, 0x76, 0xb1, 0x24, 0x20, 0x67, 0xde, 0x28, 0xcb,
+ 0x0e, 0xe6, 0x52, 0xb5, 0xfa, 0xfb, 0x8b, 0x1e, 0x6a, 0x1d, 0x09, 0x26,
+ 0xb9, 0xa7, 0x61, 0xba, 0xf8, 0x79, 0xd2, 0x66, 0x57, 0x28, 0xd7, 0x31,
+ 0xb5, 0x0b, 0x27, 0x19, 0x1e, 0x6f, 0x46, 0xfc, 0x54, 0x95, 0xeb, 0x78,
+ 0x01, 0xb6, 0xd9, 0x79, 0x5a, 0x4d, 0x02, 0x81, 0x81, 0x00, 0xd5, 0x8f,
+ 0x16, 0x53, 0x2f, 0x57, 0x93, 0xbf, 0x09, 0x75, 0xbf, 0x63, 0x40, 0x3d,
+ 0x27, 0xfd, 0x23, 0x21, 0xde, 0x9b, 0xe9, 0x73, 0x3f, 0x49, 0x02, 0xd2,
+ 0x38, 0x96, 0xcf, 0xc3, 0xba, 0x92, 0x07, 0x87, 0x52, 0xa9, 0x35, 0xe3,
+ 0x0c, 0xe4, 0x2f, 0x05, 0x7b, 0x37, 0xa5, 0x40, 0x9c, 0x3b, 0x94, 0xf7,
+ 0xad, 0xa0, 0xee, 0x3a, 0xa8, 0xfb, 0x1f, 0x11, 0x1f, 0xd8, 0x9a, 0x80,
+ 0x42, 0x3d, 0x7f, 0xa4, 0xb8, 0x9a, 0xaa, 0xea, 0x72, 0xc1, 0xe3, 0xed,
+ 0x06, 0x60, 0x92, 0x37, 0xf9, 0xba, 0xfb, 0x9e, 0xed, 0x05, 0xa6, 0xd4,
+ 0x72, 0x68, 0x4f, 0x63, 0xfe, 0xd6, 0x10, 0x0d, 0x4f, 0x0a, 0x93, 0xc6,
+ 0xb9, 0xd7, 0xaf, 0xfd, 0xd9, 0x57, 0x7d, 0xcb, 0x75, 0xe8, 0x93, 0x2b,
+ 0xae, 0x4f, 0xea, 0xd7, 0x30, 0x0b, 0x58, 0x44, 0x82, 0x0f, 0x84, 0x5d,
+ 0x62, 0x11, 0x78, 0xea, 0x5f, 0xc5, 0x02, 0x81, 0x81, 0x00, 0x82, 0x0c,
+ 0xc1, 0xe6, 0x0b, 0x72, 0xf1, 0x48, 0x5f, 0xac, 0xbd, 0x98, 0xe5, 0x7d,
+ 0x09, 0xbd, 0x15, 0x95, 0x47, 0x09, 0xa1, 0x6c, 0x03, 0x91, 0xbf, 0x05,
+ 0x70, 0xc1, 0x3e, 0x52, 0x64, 0x99, 0x0e, 0xa7, 0x98, 0x70, 0xfb, 0xf6,
+ 0xeb, 0x9e, 0x25, 0x9d, 0x8e, 0x88, 0x30, 0xf2, 0xf0, 0x22, 0x6c, 0xd0,
+ 0xcc, 0x51, 0x8f, 0x5c, 0x70, 0xc7, 0x37, 0xc4, 0x69, 0xab, 0x1d, 0xfc,
+ 0xed, 0x3a, 0x03, 0xbb, 0xa2, 0xad, 0xb6, 0xea, 0x89, 0x6b, 0x67, 0x4b,
+ 0x96, 0xaa, 0xd9, 0xcc, 0xc8, 0x4b, 0xfa, 0x18, 0x21, 0x08, 0xb2, 0xa3,
+ 0xb9, 0x3e, 0x61, 0x99, 0xdc, 0x5a, 0x97, 0x9c, 0x73, 0x6a, 0xb9, 0xf9,
+ 0x68, 0x03, 0x24, 0x5f, 0x55, 0x77, 0x9c, 0xb4, 0xbe, 0x7a, 0x78, 0x53,
+ 0x68, 0x48, 0x69, 0x53, 0xc8, 0xb1, 0xf5, 0xbf, 0x98, 0x2d, 0x11, 0x1e,
+ 0x98, 0xa8, 0x36, 0x50, 0xa0, 0xb1, 0x02, 0x81, 0x81, 0x00, 0x90, 0x88,
+ 0x30, 0x71, 0xc7, 0xfe, 0x9b, 0x6d, 0x95, 0x37, 0x6d, 0x79, 0xfc, 0x85,
+ 0xe7, 0x44, 0x78, 0xbc, 0x79, 0x6e, 0x47, 0x86, 0xc9, 0xf3, 0xdd, 0xc6,
+ 0xec, 0xa9, 0x94, 0x9f, 0x40, 0xeb, 0x87, 0xd0, 0xdb, 0xee, 0xcd, 0x1b,
+ 0x87, 0x23, 0xff, 0x76, 0xd4, 0x37, 0x8a, 0xcd, 0xb9, 0x6e, 0xd1, 0x98,
+ 0xf6, 0x97, 0x8d, 0xe3, 0x81, 0x6d, 0xc3, 0x4e, 0xd1, 0xa0, 0xc4, 0x9f,
+ 0xbd, 0x34, 0xe5, 0xe8, 0x53, 0x4f, 0xca, 0x10, 0xb5, 0xed, 0xe7, 0x16,
+ 0x09, 0x54, 0xde, 0x60, 0xa7, 0xd1, 0x16, 0x6e, 0x2e, 0xb7, 0xbe, 0x7a,
+ 0xd5, 0x9b, 0x26, 0xef, 0xe4, 0x0e, 0x77, 0xfa, 0xa9, 0xdd, 0xdc, 0xb9,
+ 0x88, 0x19, 0x23, 0x70, 0xc7, 0xe1, 0x60, 0xaf, 0x8c, 0x73, 0x04, 0xf7,
+ 0x71, 0x17, 0x81, 0x36, 0x75, 0xbb, 0x97, 0xd7, 0x75, 0xb6, 0x8e, 0xbc,
+ 0xac, 0x9c, 0x6a, 0x9b, 0x24, 0x89, 0x02, 0x81, 0x80, 0x5a, 0x2b, 0xc7,
+ 0x6b, 0x8c, 0x65, 0xdb, 0x04, 0x73, 0xab, 0x25, 0xe1, 0x5b, 0xbc, 0x3c,
+ 0xcf, 0x5a, 0x3c, 0x04, 0xae, 0x97, 0x2e, 0xfd, 0xa4, 0x97, 0x1f, 0x05,
+ 0x17, 0x27, 0xac, 0x7c, 0x30, 0x85, 0xb4, 0x82, 0x3f, 0x5b, 0xb7, 0x94,
+ 0x3b, 0x7f, 0x6c, 0x0c, 0xc7, 0x16, 0xc6, 0xa0, 0xbd, 0x80, 0xb0, 0x81,
+ 0xde, 0xa0, 0x23, 0xa6, 0xf6, 0x75, 0x33, 0x51, 0x35, 0xa2, 0x75, 0x55,
+ 0x70, 0x4d, 0x42, 0xbb, 0xcf, 0x54, 0xe4, 0xdb, 0x2d, 0x88, 0xa0, 0x7a,
+ 0xf2, 0x17, 0xa7, 0xdd, 0x13, 0x44, 0x9f, 0x5f, 0x6b, 0x2c, 0x42, 0x42,
+ 0x8b, 0x13, 0x4d, 0xf9, 0x5b, 0xf8, 0x33, 0x42, 0xd9, 0x9e, 0x50, 0x1c,
+ 0x7c, 0xbc, 0xfa, 0x62, 0x85, 0x0b, 0xcf, 0x99, 0xda, 0x9e, 0x04, 0x90,
+ 0xb2, 0xc6, 0xb2, 0x0a, 0x2a, 0x7c, 0x6d, 0x6a, 0x40, 0xfc, 0xf5, 0x50,
+ 0x98, 0x46, 0x89, 0x82, 0x40,
+};
+
+
+#endif // HEADER_SSL_TEST_FUZZER
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 0a6648ff..9e7b2044 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -26,9 +26,27 @@ const (
VersionTLS13 = 0x0304
)
+const (
+ VersionDTLS10 = 0xfeff
+ VersionDTLS12 = 0xfefd
+)
+
// A draft version of TLS 1.3 that is sent over the wire for the current draft.
const tls13DraftVersion = 0x7f12
+var allTLSWireVersions = []uint16{
+ tls13DraftVersion,
+ VersionTLS12,
+ VersionTLS11,
+ VersionTLS10,
+ VersionSSL30,
+}
+
+var allDTLSWireVersions = []uint16{
+ VersionDTLS12,
+ VersionDTLS10,
+}
+
const (
maxPlaintext = 16384 // maximum plaintext payload length
maxCiphertext = 16384 + 2048 // maximum ciphertext payload length
@@ -625,12 +643,12 @@ type ProtocolBugs struct {
SendSupportedVersions []uint16
// NegotiateVersion, if non-zero, causes the server to negotiate the
- // specifed TLS version rather than the version supported by either
+ // specifed wire version rather than the version supported by either
// peer.
NegotiateVersion uint16
// NegotiateVersionOnRenego, if non-zero, causes the server to negotiate
- // the specified TLS version on renegotiation rather than retaining it.
+ // the specified wire version on renegotiation rather than retaining it.
NegotiateVersionOnRenego uint16
// ExpectFalseStart causes the server to, on full handshakes,
@@ -710,9 +728,13 @@ type ProtocolBugs struct {
EmptyRenegotiationInfo bool
// BadRenegotiationInfo causes the renegotiation extension value in a
- // renegotiation handshake to be incorrect.
+ // renegotiation handshake to be incorrect at the start.
BadRenegotiationInfo bool
+ // BadRenegotiationInfoEnd causes the renegotiation extension value in
+ // a renegotiation handshake to be incorrect at the end.
+ BadRenegotiationInfoEnd bool
+
// NoRenegotiationInfo disables renegotiation info support in all
// handshakes.
NoRenegotiationInfo bool
@@ -1257,6 +1279,10 @@ type ProtocolBugs struct {
// send in the ClientHello.
SendCompressionMethods []byte
+ // SendCompressionMethod is the compression method to send in the
+ // ServerHello.
+ SendCompressionMethod byte
+
// AlwaysSendPreSharedKeyIdentityHint, if true, causes the server to
// always send a ServerKeyExchange for PSK ciphers, even if the identity
// hint is empty.
@@ -1438,10 +1464,29 @@ func (c *Config) defaultCurves() map[CurveID]bool {
return defaultCurves
}
-// isSupportedVersion returns true if the specified protocol version is
-// acceptable.
-func (c *Config) isSupportedVersion(vers uint16, isDTLS bool) bool {
- return c.minVersion(isDTLS) <= vers && vers <= c.maxVersion(isDTLS)
+// isSupportedVersion checks if the specified wire version is acceptable. If so,
+// it returns true and the corresponding protocol version. Otherwise, it returns
+// false.
+func (c *Config) isSupportedVersion(wireVers uint16, isDTLS bool) (uint16, bool) {
+ vers, ok := wireToVersion(wireVers, isDTLS)
+ if !ok || c.minVersion(isDTLS) > vers || vers > c.maxVersion(isDTLS) {
+ return 0, false
+ }
+ return vers, true
+}
+
+func (c *Config) supportedVersions(isDTLS bool) []uint16 {
+ versions := allTLSWireVersions
+ if isDTLS {
+ versions = allDTLSWireVersions
+ }
+ var ret []uint16
+ for _, vers := range versions {
+ if _, ok := c.isSupportedVersion(vers, isDTLS); ok {
+ ret = append(ret, vers)
+ }
+ }
+ return ret
}
// getCertificateForName returns the best certificate for the given name,
@@ -1717,3 +1762,12 @@ var (
downgradeTLS13 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01}
downgradeTLS12 = []byte{0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x00}
)
+
+func containsGREASE(values []uint16) bool {
+ for _, v := range values {
+ if isGREASEValue(v) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index fce00495..61fc9d31 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -35,6 +35,7 @@ type Conn struct {
// constant after handshake; protected by handshakeMutex
handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
handshakeErr error // error resulting from handshake
+ wireVersion uint16 // TLS wire version
vers uint16 // TLS version
haveVers bool // version has been negotiated
config *Config // configuration passed to constructor
diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go
index e273bc79..d46b247a 100644
--- a/src/ssl/test/runner/dtls.go
+++ b/src/ssl/test/runner/dtls.go
@@ -23,32 +23,12 @@ import (
"net"
)
-func versionToWire(vers uint16, isDTLS bool) uint16 {
- if isDTLS {
- switch vers {
- case VersionTLS12:
- return 0xfefd
- case VersionTLS10:
- return 0xfeff
- }
- } else {
- switch vers {
- case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
- return vers
- case VersionTLS13:
- return tls13DraftVersion
- }
- }
-
- panic("unknown version")
-}
-
func wireToVersion(vers uint16, isDTLS bool) (uint16, bool) {
if isDTLS {
switch vers {
- case 0xfefd:
+ case VersionDTLS12:
return VersionTLS12, true
- case 0xfeff:
+ case VersionDTLS10:
return VersionTLS10, true
}
} else {
@@ -102,9 +82,9 @@ func (c *Conn) dtlsDoReadRecord(want recordType) (recordType, *block, error) {
// version is irrelevant.)
if typ != recordTypeAlert {
if c.haveVers {
- if wireVers := versionToWire(c.vers, c.isDTLS); vers != wireVers {
+ if vers != c.wireVersion {
c.sendAlert(alertProtocolVersion)
- return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, wireVers))
+ return 0, nil, c.in.setErrorLocked(fmt.Errorf("dtls: received record with version %x when expecting version %x", vers, c.wireVersion))
}
} else {
// Pre-version-negotiation alerts may be sent with any version.
@@ -368,13 +348,16 @@ func (c *Conn) dtlsSealRecord(typ recordType, data []byte) (b *block, err error)
// TODO(nharper): DTLS 1.3 will likely need to set this to
// recordTypeApplicationData if c.out.cipher != nil.
b.data[0] = byte(typ)
- vers := c.vers
+ vers := c.wireVersion
if vers == 0 {
// Some TLS servers fail if the record version is greater than
// TLS 1.0 for the initial ClientHello.
- vers = VersionTLS10
+ if c.isDTLS {
+ vers = VersionDTLS10
+ } else {
+ 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.
diff --git a/src/ssl/test/runner/fuzzer_mode.json b/src/ssl/test/runner/fuzzer_mode.json
index fd819f96..d2f64efc 100644
--- a/src/ssl/test/runner/fuzzer_mode.json
+++ b/src/ssl/test/runner/fuzzer_mode.json
@@ -46,6 +46,8 @@
"TLS13-EarlyData-ALPNOmitted2-Server": "Trial decryption does not work with the NULL cipher.",
"TLS13-EarlyData-RejectUnfinishedWrite-Client-*": "Trial decryption does not work with the NULL cipher.",
"TLS13-EarlyData-Reject-Client": "Trial decryption does not work with the NULL cipher.",
- "TLS13-EarlyData-RejectTicket-Client": "Trial decryption does not work with the NULL cipher."
+ "TLS13-EarlyData-RejectTicket-Client": "Trial decryption does not work with the NULL cipher.",
+
+ "Renegotiate-Client-BadExt*": "Fuzzer mode does not check renegotiation_info."
}
}
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index c531a281..f0bfca48 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -35,6 +35,21 @@ type clientHandshakeState struct {
finishedBytes []byte
}
+func mapClientHelloVersion(vers uint16, isDTLS bool) uint16 {
+ if !isDTLS {
+ return vers
+ }
+
+ switch vers {
+ case VersionTLS12:
+ return VersionDTLS12
+ case VersionTLS10:
+ return VersionDTLS10
+ }
+
+ panic("Unknown ClientHello version.")
+}
+
func (c *Conn) clientHandshake() error {
if c.config == nil {
c.config = defaultConfig()
@@ -63,7 +78,6 @@ func (c *Conn) clientHandshake() error {
maxVersion := c.config.maxVersion(c.isDTLS)
hello := &clientHelloMsg{
isDTLS: c.isDTLS,
- vers: versionToWire(maxVersion, c.isDTLS),
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: !c.config.Bugs.NoOCSPStapling,
@@ -85,6 +99,23 @@ func (c *Conn) clientHandshake() error {
pskBinderFirst: c.config.Bugs.PSKBinderFirst,
}
+ if maxVersion >= VersionTLS13 {
+ hello.vers = mapClientHelloVersion(VersionTLS12, c.isDTLS)
+ if !c.config.Bugs.OmitSupportedVersions {
+ hello.supportedVersions = c.config.supportedVersions(c.isDTLS)
+ }
+ } else {
+ hello.vers = mapClientHelloVersion(maxVersion, c.isDTLS)
+ }
+
+ if c.config.Bugs.SendClientVersion != 0 {
+ hello.vers = c.config.Bugs.SendClientVersion
+ }
+
+ if len(c.config.Bugs.SendSupportedVersions) > 0 {
+ hello.supportedVersions = c.config.Bugs.SendSupportedVersions
+ }
+
disableEMS := c.config.Bugs.NoExtendedMasterSecret
if c.cipherSuite != nil {
disableEMS = c.config.Bugs.NoExtendedMasterSecretOnRenegotiation
@@ -310,23 +341,6 @@ NextCipherSuite:
}
}
- if maxVersion == VersionTLS13 && !c.config.Bugs.OmitSupportedVersions {
- if hello.vers >= VersionTLS13 {
- hello.vers = VersionTLS12
- }
- for version := maxVersion; version >= minVersion; version-- {
- hello.supportedVersions = append(hello.supportedVersions, versionToWire(version, c.isDTLS))
- }
- }
-
- if len(c.config.Bugs.SendSupportedVersions) > 0 {
- hello.supportedVersions = c.config.Bugs.SendSupportedVersions
- }
-
- if c.config.Bugs.SendClientVersion != 0 {
- hello.vers = c.config.Bugs.SendClientVersion
- }
-
if c.config.Bugs.SendCipherSuites != nil {
hello.cipherSuites = c.config.Bugs.SendCipherSuites
}
@@ -409,7 +423,7 @@ NextCipherSuite:
if c.isDTLS {
helloVerifyRequest, ok := msg.(*helloVerifyRequestMsg)
if ok {
- if helloVerifyRequest.vers != versionToWire(VersionTLS10, c.isDTLS) {
+ if helloVerifyRequest.vers != VersionDTLS10 {
// Per RFC 6347, the version field in
// HelloVerifyRequest SHOULD be always DTLS
// 1.0. Enforce this for testing purposes.
@@ -443,14 +457,12 @@ NextCipherSuite:
return fmt.Errorf("tls: received unexpected message of type %T when waiting for HelloRetryRequest or ServerHello", msg)
}
- serverVersion, ok := wireToVersion(serverWireVersion, c.isDTLS)
- if ok {
- ok = c.config.isSupportedVersion(serverVersion, c.isDTLS)
- }
+ serverVersion, ok := c.config.isSupportedVersion(serverWireVersion, c.isDTLS)
if !ok {
c.sendAlert(alertProtocolVersion)
return fmt.Errorf("tls: server selected unsupported protocol version %x", c.vers)
}
+ c.wireVersion = serverWireVersion
c.vers = serverVersion
c.haveVers = true
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 3a182ecf..f70f4697 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -161,7 +161,7 @@ func (hs *serverHandshakeState) readClientHello() error {
// Per RFC 6347, the version field in HelloVerifyRequest SHOULD
// be always DTLS 1.0
helloVerifyRequest := &helloVerifyRequestMsg{
- vers: versionToWire(VersionTLS10, c.isDTLS),
+ vers: VersionDTLS10,
cookie: make([]byte, 32),
}
if _, err := io.ReadFull(c.config.rand(), helloVerifyRequest.cookie); err != nil {
@@ -210,74 +210,69 @@ func (hs *serverHandshakeState) readClientHello() error {
c.clientVersion = hs.clientHello.vers
- // Convert the ClientHello wire version to a protocol version.
- var clientVersion uint16
- if c.isDTLS {
- if hs.clientHello.vers <= 0xfefd {
- clientVersion = VersionTLS12
- } else if hs.clientHello.vers <= 0xfeff {
- clientVersion = VersionTLS10
- }
- } else {
- if hs.clientHello.vers >= VersionTLS12 {
- clientVersion = VersionTLS12
- } else if hs.clientHello.vers >= VersionTLS11 {
- clientVersion = VersionTLS11
- } else if hs.clientHello.vers >= VersionTLS10 {
- clientVersion = VersionTLS10
- } else if hs.clientHello.vers >= VersionSSL30 {
- clientVersion = VersionSSL30
- }
- }
-
- if config.Bugs.NegotiateVersion != 0 {
- c.vers = config.Bugs.NegotiateVersion
- } else if c.haveVers && config.Bugs.NegotiateVersionOnRenego != 0 {
- c.vers = config.Bugs.NegotiateVersionOnRenego
- } else if len(hs.clientHello.supportedVersions) > 0 {
- // Use the versions extension if supplied.
- var foundVersion, foundGREASE bool
- for _, extVersion := range hs.clientHello.supportedVersions {
- if isGREASEValue(extVersion) {
- foundGREASE = true
- }
- extVersion, ok = wireToVersion(extVersion, c.isDTLS)
- if !ok {
- continue
+ // Use the versions extension if supplied, otherwise use the legacy ClientHello version.
+ if len(hs.clientHello.supportedVersions) == 0 {
+ if c.isDTLS {
+ if hs.clientHello.vers <= VersionDTLS12 {
+ hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionDTLS12)
}
- if config.isSupportedVersion(extVersion, c.isDTLS) && !foundVersion {
- c.vers = extVersion
- foundVersion = true
- break
+ if hs.clientHello.vers <= VersionDTLS10 {
+ hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionDTLS10)
+ }
+ } else {
+ if hs.clientHello.vers >= VersionTLS12 {
+ hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionTLS12)
+ }
+ if hs.clientHello.vers >= VersionTLS11 {
+ hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionTLS11)
+ }
+ if hs.clientHello.vers >= VersionTLS10 {
+ hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionTLS10)
+ }
+ if hs.clientHello.vers >= VersionSSL30 {
+ hs.clientHello.supportedVersions = append(hs.clientHello.supportedVersions, VersionSSL30)
}
}
- if !foundVersion {
- c.sendAlert(alertProtocolVersion)
- return errors.New("tls: client did not offer any supported protocol versions")
- }
- if config.Bugs.ExpectGREASE && !foundGREASE {
- return errors.New("tls: no GREASE version value found")
- }
- } else {
- // Otherwise, use the legacy ClientHello version.
- version := clientVersion
- if maxVersion := config.maxVersion(c.isDTLS); version > maxVersion {
- version = maxVersion
- }
- if version == 0 || !config.isSupportedVersion(version, c.isDTLS) {
- return fmt.Errorf("tls: client offered an unsupported, maximum protocol version of %x", hs.clientHello.vers)
+ } else if config.Bugs.ExpectGREASE && !containsGREASE(hs.clientHello.supportedVersions) {
+ return errors.New("tls: no GREASE version value found")
+ }
+
+ if !c.haveVers {
+ if config.Bugs.NegotiateVersion != 0 {
+ c.wireVersion = config.Bugs.NegotiateVersion
+ } else {
+ var found bool
+ for _, vers := range hs.clientHello.supportedVersions {
+ if _, ok := config.isSupportedVersion(vers, c.isDTLS); ok {
+ c.wireVersion = vers
+ found = true
+ break
+ }
+ }
+ if !found {
+ c.sendAlert(alertProtocolVersion)
+ return errors.New("tls: client did not offer any supported protocol versions")
+ }
}
- c.vers = version
+ } else if config.Bugs.NegotiateVersionOnRenego != 0 {
+ c.wireVersion = config.Bugs.NegotiateVersionOnRenego
+ }
+
+ c.vers, ok = wireToVersion(c.wireVersion, c.isDTLS)
+ if !ok {
+ panic("Could not map wire version")
}
c.haveVers = true
+ clientProtocol, ok := wireToVersion(c.clientVersion, c.isDTLS)
+
// Reject < 1.2 ClientHellos with signature_algorithms.
- if clientVersion < VersionTLS12 && len(hs.clientHello.signatureAlgorithms) > 0 {
+ if ok && clientProtocol < VersionTLS12 && len(hs.clientHello.signatureAlgorithms) > 0 {
return fmt.Errorf("tls: client included signature_algorithms before TLS 1.2")
}
// Check the client cipher list is consistent with the version.
- if clientVersion < VersionTLS12 {
+ if ok && clientProtocol < VersionTLS12 {
for _, id := range hs.clientHello.cipherSuites {
if isTLS12Cipher(id) {
return fmt.Errorf("tls: client offered TLS 1.2 cipher before TLS 1.2")
@@ -298,13 +293,11 @@ func (hs *serverHandshakeState) readClientHello() error {
return fmt.Errorf("tls: client offered unexpected PSK identities")
}
- var scsvFound, greaseFound bool
+ var scsvFound bool
for _, cipherSuite := range hs.clientHello.cipherSuites {
if cipherSuite == fallbackSCSV {
scsvFound = true
- }
- if isGREASEValue(cipherSuite) {
- greaseFound = true
+ break
}
}
@@ -314,11 +307,11 @@ func (hs *serverHandshakeState) readClientHello() error {
return errors.New("tls: fallback SCSV found when not expected")
}
- if !greaseFound && config.Bugs.ExpectGREASE {
+ if config.Bugs.ExpectGREASE && !containsGREASE(hs.clientHello.cipherSuites) {
return errors.New("tls: no GREASE cipher suite value found")
}
- greaseFound = false
+ var greaseFound bool
for _, curve := range hs.clientHello.supportedCurves {
if isGREASEValue(uint16(curve)) {
greaseFound = true
@@ -367,7 +360,7 @@ func (hs *serverHandshakeState) doTLS13Handshake() error {
hs.hello = &serverHelloMsg{
isDTLS: c.isDTLS,
- vers: versionToWire(c.vers, c.isDTLS),
+ vers: c.wireVersion,
versOverride: config.Bugs.SendServerHelloVersion,
customExtension: config.Bugs.CustomUnencryptedExtension,
unencryptedALPN: config.Bugs.SendUnencryptedALPN,
@@ -526,7 +519,7 @@ Curves:
ResendHelloRetryRequest:
var sendHelloRetryRequest bool
helloRetryRequest := &helloRetryRequestMsg{
- vers: versionToWire(c.vers, c.isDTLS),
+ vers: c.wireVersion,
duplicateExtensions: config.Bugs.DuplicateHelloRetryRequestExtensions,
}
@@ -1049,9 +1042,9 @@ func (hs *serverHandshakeState) processClientHello() (isResume bool, err error)
hs.hello = &serverHelloMsg{
isDTLS: c.isDTLS,
- vers: versionToWire(c.vers, c.isDTLS),
+ vers: c.wireVersion,
versOverride: config.Bugs.SendServerHelloVersion,
- compressionMethod: compressionNone,
+ compressionMethod: config.Bugs.SendCompressionMethod,
}
hs.hello.random = make([]byte, 32)
@@ -1168,6 +1161,9 @@ func (hs *serverHandshakeState) processClientExtensions(serverExtensions *server
if c.config.Bugs.BadRenegotiationInfo {
serverExtensions.secureRenegotiation[0] ^= 0x80
}
+ if c.config.Bugs.BadRenegotiationInfoEnd {
+ serverExtensions.secureRenegotiation[len(serverExtensions.secureRenegotiation)-1] ^= 0x80
+ }
} else {
serverExtensions.secureRenegotiation = hs.clientHello.secureRenegotiation
}
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index d9218f26..03ba7555 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -263,6 +263,31 @@ func getShimKey(t testCert) string {
panic("Unknown test certificate")
}
+// configVersionToWire maps a protocol version to the default wire version to
+// test at that protocol.
+//
+// TODO(davidben): Rather than mapping these, make tlsVersions contains a list
+// of wire versions and test all of them.
+func configVersionToWire(vers uint16, protocol protocol) uint16 {
+ if protocol == dtls {
+ switch vers {
+ case VersionTLS12:
+ return VersionDTLS12
+ case VersionTLS10:
+ return VersionDTLS10
+ }
+ } else {
+ switch vers {
+ case VersionSSL30, VersionTLS10, VersionTLS11, VersionTLS12:
+ return vers
+ case VersionTLS13:
+ return tls13DraftVersion
+ }
+ }
+
+ panic("unknown version")
+}
+
// encodeDERValues encodes a series of bytestrings in comma-separated-hex form.
func encodeDERValues(values [][]byte) string {
var ret string
@@ -440,30 +465,20 @@ type testCase struct {
var testCases []testCase
-func writeTranscript(test *testCase, num int, data []byte) {
+func writeTranscript(test *testCase, path string, data []byte) {
if len(data) == 0 {
return
}
- protocol := "tls"
- if test.protocol == dtls {
- protocol = "dtls"
- }
-
- side := "client"
- if test.testType == serverTest {
- side = "server"
- }
-
- dir := path.Join(*transcriptDir, protocol, side)
- if err := os.MkdirAll(dir, 0755); err != nil {
- fmt.Fprintf(os.Stderr, "Error making %s: %s\n", dir, err)
+ settings, err := ioutil.ReadFile(path)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Error reading %s: %s.\n", path, err)
return
}
- name := fmt.Sprintf("%s-%d", test.name, num)
- if err := ioutil.WriteFile(path.Join(dir, name), data, 0644); err != nil {
- fmt.Fprintf(os.Stderr, "Error writing %s: %s\n", name, err)
+ settings = append(settings, data...)
+ if err := ioutil.WriteFile(path, settings, 0644); err != nil {
+ fmt.Fprintf(os.Stderr, "Error writing %s: %s\n", path, err)
}
}
@@ -487,7 +502,7 @@ func (t *timeoutConn) Write(b []byte) (int, error) {
return t.Conn.Write(b)
}
-func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, num int) error {
+func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, transcriptPrefix string, num int) error {
if !test.noSessionCache {
if config.ClientSessionCache == nil {
config.ClientSessionCache = NewLRUClientSessionCache(1)
@@ -536,9 +551,10 @@ func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, nu
if *flagDebug {
defer connDebug.WriteTo(os.Stdout)
}
- if len(*transcriptDir) != 0 {
+ if len(transcriptPrefix) != 0 {
defer func() {
- writeTranscript(test, num, connDebug.Transcript())
+ path := transcriptPrefix + strconv.Itoa(num)
+ writeTranscript(test, path, connDebug.Transcript())
}()
}
@@ -613,6 +629,8 @@ func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool, nu
channelIDKey.Y.Cmp(channelIDKey.Y) != 0 {
return fmt.Errorf("incorrect channel ID")
}
+ } else if connState.ChannelID != nil {
+ return fmt.Errorf("channel ID unexpectedly negotiated")
}
if expected := test.expectedNextProto; expected != "" {
@@ -1000,6 +1018,26 @@ func runTest(test *testCase, shimPath string, mallocNumToFail int64) error {
flags = append(flags, "-tls-unique")
}
+ var transcriptPrefix string
+ if len(*transcriptDir) != 0 {
+ protocol := "tls"
+ if test.protocol == dtls {
+ protocol = "dtls"
+ }
+
+ side := "client"
+ if test.testType == serverTest {
+ side = "server"
+ }
+
+ dir := filepath.Join(*transcriptDir, protocol, side)
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ return err
+ }
+ transcriptPrefix = filepath.Join(dir, test.name+"-")
+ flags = append(flags, "-write-settings", transcriptPrefix)
+ }
+
flags = append(flags, test.flags...)
var shim *exec.Cmd
@@ -1039,7 +1077,7 @@ func runTest(test *testCase, shimPath string, mallocNumToFail int64) error {
conn, err := acceptOrWait(listener, waitChan)
if err == nil {
- err = doExchange(test, &config, conn, false /* not a resumption */, 0)
+ err = doExchange(test, &config, conn, false /* not a resumption */, transcriptPrefix, 0)
conn.Close()
}
@@ -1059,7 +1097,7 @@ func runTest(test *testCase, shimPath string, mallocNumToFail int64) error {
var connResume net.Conn
connResume, err = acceptOrWait(listener, waitChan)
if err == nil {
- err = doExchange(test, &resumeConfig, connResume, true /* resumption */, i+1)
+ err = doExchange(test, &resumeConfig, connResume, true /* resumption */, transcriptPrefix, i+1)
connResume.Close()
}
}
@@ -1155,16 +1193,27 @@ func runTest(test *testCase, shimPath string, mallocNumToFail int64) error {
type tlsVersion struct {
name string
version uint16
- flag string
- hasDTLS bool
+ // excludeFlag is the legacy shim flag to disable the version.
+ excludeFlag string
+ hasDTLS bool
+ // shimTLS and shimDTLS are values the shim uses to refer to these
+ // versions in TLS and DTLS, respectively.
+ shimTLS, shimDTLS int
+}
+
+func (vers tlsVersion) shimFlag(protocol protocol) string {
+ if protocol == dtls {
+ return strconv.Itoa(vers.shimDTLS)
+ }
+ return strconv.Itoa(vers.shimTLS)
}
var tlsVersions = []tlsVersion{
- {"SSL3", VersionSSL30, "-no-ssl3", false},
- {"TLS1", VersionTLS10, "-no-tls1", true},
- {"TLS11", VersionTLS11, "-no-tls11", false},
- {"TLS12", VersionTLS12, "-no-tls12", true},
- {"TLS13", VersionTLS13, "-no-tls13", false},
+ {"SSL3", VersionSSL30, "-no-ssl3", false, VersionSSL30, 0},
+ {"TLS1", VersionTLS10, "-no-tls1", true, VersionTLS10, VersionDTLS10},
+ {"TLS11", VersionTLS11, "-no-tls11", false, VersionTLS11, 0},
+ {"TLS12", VersionTLS12, "-no-tls12", true, VersionTLS12, VersionDTLS12},
+ {"TLS13", VersionTLS13, "-no-tls13", false, VersionTLS13, 0},
}
type testCipherSuite struct {
@@ -2461,6 +2510,21 @@ func addBasicTests() {
expectedError: ":INVALID_COMPRESSION_LIST:",
expectedLocalError: "remote error: illegal parameter",
},
+ // Test that the client rejects invalid compression methods
+ // from the server.
+ {
+ testType: clientTest,
+ name: "InvalidCompressionMethod",
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ SendCompressionMethod: 1,
+ },
+ },
+ shouldFail: true,
+ expectedError: ":UNSUPPORTED_COMPRESSION_ALGORITHM:",
+ expectedLocalError: "remote error: illegal parameter",
+ },
{
name: "GREASE-Client-TLS12",
config: Config{
@@ -2957,6 +3021,116 @@ func addCipherSuiteTests() {
shouldFail: true,
expectedError: ":NO_SHARED_CIPHER:",
})
+
+ // Test cipher suite negotiation works as expected. Configure a
+ // complicated cipher suite configuration.
+ const negotiationTestCiphers = "" +
+ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:" +
+ "[TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384|TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256|TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA]:" +
+ "TLS_RSA_WITH_AES_128_GCM_SHA256:" +
+ "TLS_RSA_WITH_AES_128_CBC_SHA:" +
+ "[TLS_RSA_WITH_AES_256_GCM_SHA384|TLS_RSA_WITH_AES_256_CBC_SHA]"
+ negotiationTests := []struct {
+ ciphers []uint16
+ expected uint16
+ }{
+ // Server preferences are honored, including when
+ // equipreference groups are involved.
+ {
+ []uint16{
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ {
+ []uint16{
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ },
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ },
+ {
+ []uint16{
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ TLS_RSA_WITH_AES_128_GCM_SHA256,
+ },
+ {
+ []uint16{
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ TLS_RSA_WITH_AES_128_CBC_SHA,
+ },
+ // Equipreference groups use the client preference.
+ {
+ []uint16{
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ },
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ },
+ {
+ []uint16{
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ },
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ },
+ {
+ []uint16{
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ },
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ },
+ {
+ []uint16{
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ },
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ },
+ {
+ []uint16{
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ },
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ },
+ // If there are two equipreference groups, the preferred one
+ // takes precedence.
+ {
+ []uint16{
+ TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_RSA_WITH_AES_256_CBC_SHA,
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ },
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ },
+ }
+ for i, t := range negotiationTests {
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "CipherNegotiation-" + strconv.Itoa(i),
+ config: Config{
+ MaxVersion: VersionTLS12,
+ CipherSuites: t.ciphers,
+ },
+ flags: []string{"-cipher", negotiationTestCiphers},
+ expectedCipher: t.expected,
+ })
+ }
}
func addBadECDSASignatureTests() {
@@ -3036,36 +3210,46 @@ func addCBCPaddingTests() {
}
func addCBCSplittingTests() {
- testCases = append(testCases, testCase{
- name: "CBCRecordSplitting",
- config: Config{
- MaxVersion: VersionTLS10,
- MinVersion: VersionTLS10,
- CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
- },
- messageLen: -1, // read until EOF
- resumeSession: true,
- flags: []string{
- "-async",
- "-write-different-record-sizes",
- "-cbc-record-splitting",
- },
- })
- testCases = append(testCases, testCase{
- name: "CBCRecordSplittingPartialWrite",
- config: Config{
- MaxVersion: VersionTLS10,
- MinVersion: VersionTLS10,
- CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
- },
- messageLen: -1, // read until EOF
- flags: []string{
- "-async",
- "-write-different-record-sizes",
- "-cbc-record-splitting",
- "-partial-write",
- },
- })
+ var cbcCiphers = []struct {
+ name string
+ cipher uint16
+ }{
+ {"3DES", TLS_RSA_WITH_3DES_EDE_CBC_SHA},
+ {"AES128", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ {"AES256", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
+ }
+ for _, t := range cbcCiphers {
+ testCases = append(testCases, testCase{
+ name: "CBCRecordSplitting-" + t.name,
+ config: Config{
+ MaxVersion: VersionTLS10,
+ MinVersion: VersionTLS10,
+ CipherSuites: []uint16{t.cipher},
+ },
+ messageLen: -1, // read until EOF
+ resumeSession: true,
+ flags: []string{
+ "-async",
+ "-write-different-record-sizes",
+ "-cbc-record-splitting",
+ },
+ })
+ testCases = append(testCases, testCase{
+ name: "CBCRecordSplittingPartialWrite-" + t.name,
+ config: Config{
+ MaxVersion: VersionTLS10,
+ MinVersion: VersionTLS10,
+ CipherSuites: []uint16{t.cipher},
+ },
+ messageLen: -1, // read until EOF
+ flags: []string{
+ "-async",
+ "-write-different-record-sizes",
+ "-cbc-record-splitting",
+ "-partial-write",
+ },
+ })
+ }
}
func addClientAuthTests() {
@@ -3209,6 +3393,37 @@ func addClientAuthTests() {
shouldFail: true,
expectedError: ":UNEXPECTED_MESSAGE:",
})
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "VerifyPeerIfNoOBC-NoChannelID-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ },
+ flags: []string{
+ "-enable-channel-id",
+ "-verify-peer-if-no-obc",
+ },
+ shouldFail: true,
+ expectedError: ":PEER_DID_NOT_RETURN_A_CERTIFICATE:",
+ expectedLocalError: certificateRequired,
+ })
+
+ testCases = append(testCases, testCase{
+ testType: serverTest,
+ name: "VerifyPeerIfNoOBC-ChannelID-" + ver.name,
+ config: Config{
+ MinVersion: ver.version,
+ MaxVersion: ver.version,
+ ChannelID: channelIDKey,
+ },
+ expectChannelID: true,
+ flags: []string{
+ "-enable-channel-id",
+ "-verify-peer-if-no-obc",
+ },
+ })
}
testCases = append(testCases, testCase{
@@ -4363,6 +4578,36 @@ func addStateMachineCoverageTests(config stateMachineTestConfig) {
shouldFail: true,
expectedError: ":CHANNEL_ID_SIGNATURE_INVALID:",
})
+
+ if ver.version < VersionTLS13 {
+ // Channel ID requires ECDHE ciphers.
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "ChannelID-NoECDHE-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA},
+ ChannelID: channelIDKey,
+ },
+ expectChannelID: false,
+ flags: []string{"-enable-channel-id"},
+ })
+
+ // Sanity-check setting expectChannelID false works.
+ tests = append(tests, testCase{
+ testType: serverTest,
+ name: "ChannelID-ECDHE-" + ver.name,
+ config: Config{
+ MaxVersion: ver.version,
+ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ ChannelID: channelIDKey,
+ },
+ expectChannelID: false,
+ flags: []string{"-enable-channel-id"},
+ shouldFail: true,
+ expectedLocalError: "channel ID unexpectedly negotiated",
+ })
+ }
}
// Channel ID and NPN at the same time, to ensure their relative
@@ -4541,7 +4786,7 @@ func addVersionNegotiationTests() {
// Assemble flags to disable all newer versions on the shim.
var flags []string
for _, vers := range tlsVersions[i+1:] {
- flags = append(flags, vers.flag)
+ flags = append(flags, vers.excludeFlag)
}
// Test configuring the runner's maximum version.
@@ -4561,19 +4806,17 @@ func addVersionNegotiationTests() {
suffix += "-DTLS"
}
- shimVersFlag := strconv.Itoa(int(versionToWire(shimVers.version, protocol == dtls)))
-
// Determine the expected initial record-layer versions.
clientVers := shimVers.version
if clientVers > VersionTLS10 {
clientVers = VersionTLS10
}
- clientVers = versionToWire(clientVers, protocol == dtls)
+ clientVers = configVersionToWire(clientVers, protocol)
serverVers := expectedVersion
if expectedVersion >= VersionTLS13 {
serverVers = VersionTLS10
}
- serverVers = versionToWire(serverVers, protocol == dtls)
+ serverVers = configVersionToWire(serverVers, protocol)
testCases = append(testCases, testCase{
protocol: protocol,
@@ -4598,7 +4841,7 @@ func addVersionNegotiationTests() {
ExpectInitialRecordVersion: clientVers,
},
},
- flags: []string{"-max-version", shimVersFlag},
+ flags: []string{"-max-version", shimVers.shimFlag(protocol)},
expectedVersion: expectedVersion,
})
@@ -4625,7 +4868,7 @@ func addVersionNegotiationTests() {
ExpectInitialRecordVersion: serverVers,
},
},
- flags: []string{"-max-version", shimVersFlag},
+ flags: []string{"-max-version", shimVers.shimFlag(protocol)},
expectedVersion: expectedVersion,
})
}
@@ -4644,7 +4887,7 @@ func addVersionNegotiationTests() {
suffix += "-DTLS"
}
- wireVersion := versionToWire(vers.version, protocol == dtls)
+ wireVersion := configVersionToWire(vers.version, protocol)
testCases = append(testCases, testCase{
protocol: protocol,
testType: serverTest,
@@ -4882,7 +5125,7 @@ func addMinimumVersionTests() {
// Assemble flags to disable all older versions on the shim.
var flags []string
for _, vers := range tlsVersions[:i] {
- flags = append(flags, vers.flag)
+ flags = append(flags, vers.excludeFlag)
}
for _, runnerVers := range tlsVersions {
@@ -4895,7 +5138,6 @@ func addMinimumVersionTests() {
if protocol == dtls {
suffix += "-DTLS"
}
- shimVersFlag := strconv.Itoa(int(versionToWire(shimVers.version, protocol == dtls)))
var expectedVersion uint16
var shouldFail bool
@@ -4918,7 +5160,7 @@ func addMinimumVersionTests() {
// Ensure the server does not decline to
// select a version (versions extension) or
// cipher (some ciphers depend on versions).
- NegotiateVersion: runnerVers.version,
+ NegotiateVersion: configVersionToWire(runnerVers.version, protocol),
IgnorePeerCipherPreferences: shouldFail,
},
},
@@ -4938,11 +5180,11 @@ func addMinimumVersionTests() {
// Ensure the server does not decline to
// select a version (versions extension) or
// cipher (some ciphers depend on versions).
- NegotiateVersion: runnerVers.version,
+ NegotiateVersion: configVersionToWire(runnerVers.version, protocol),
IgnorePeerCipherPreferences: shouldFail,
},
},
- flags: []string{"-min-version", shimVersFlag},
+ flags: []string{"-min-version", shimVers.shimFlag(protocol)},
expectedVersion: expectedVersion,
shouldFail: shouldFail,
expectedError: expectedError,
@@ -4969,7 +5211,7 @@ func addMinimumVersionTests() {
config: Config{
MaxVersion: runnerVers.version,
},
- flags: []string{"-min-version", shimVersFlag},
+ flags: []string{"-min-version", shimVers.shimFlag(protocol)},
expectedVersion: expectedVersion,
shouldFail: shouldFail,
expectedError: expectedError,
@@ -6464,6 +6706,19 @@ func addRenegotiationTests() {
expectedError: ":RENEGOTIATION_MISMATCH:",
})
testCases = append(testCases, testCase{
+ name: "Renegotiate-Client-BadExt2",
+ renegotiate: 1,
+ config: Config{
+ MaxVersion: VersionTLS12,
+ Bugs: ProtocolBugs{
+ BadRenegotiationInfoEnd: true,
+ },
+ },
+ flags: []string{"-renegotiate-freely"},
+ shouldFail: true,
+ expectedError: ":RENEGOTIATION_MISMATCH:",
+ })
+ testCases = append(testCases, testCase{
name: "Renegotiate-Client-Downgrade",
renegotiate: 1,
config: Config{
@@ -10900,6 +11155,24 @@ func addTLS13HandshakeTests() {
"-enable-early-data",
},
})
+
+ // Test that the client reports TLS 1.3 as the version while sending
+ // early data.
+ testCases = append(testCases, testCase{
+ testType: clientTest,
+ name: "TLS13-EarlyData-Client-VersionAPI",
+ config: Config{
+ MaxVersion: VersionTLS13,
+ MaxEarlyDataSize: 16384,
+ },
+ resumeSession: true,
+ flags: []string{
+ "-enable-early-data",
+ "-expect-early-data-info",
+ "-expect-accept-early-data",
+ "-expect-version", strconv.Itoa(VersionTLS13),
+ },
+ })
}
func addTLS13CipherPreferenceTests() {
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 960240e1..195371fb 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -95,6 +95,7 @@ const Flag<bool> kBoolFlags[] = {
{ "-shim-shuts-down", &TestConfig::shim_shuts_down },
{ "-verify-fail", &TestConfig::verify_fail },
{ "-verify-peer", &TestConfig::verify_peer },
+ { "-verify-peer-if-no-obc", &TestConfig::verify_peer_if_no_obc },
{ "-expect-verify-result", &TestConfig::expect_verify_result },
{ "-renegotiate-once", &TestConfig::renegotiate_once },
{ "-renegotiate-freely", &TestConfig::renegotiate_freely },
@@ -131,6 +132,7 @@ const Flag<bool> kBoolFlags[] = {
};
const Flag<std::string> kStringFlags[] = {
+ { "-write-settings", &TestConfig::write_settings },
{ "-digest-prefs", &TestConfig::digest_prefs },
{ "-key-file", &TestConfig::key_file },
{ "-cert-file", &TestConfig::cert_file },
@@ -172,6 +174,7 @@ const Flag<int> kIntFlags[] = {
{ "-resume-count", &TestConfig::resume_count },
{ "-min-version", &TestConfig::min_version },
{ "-max-version", &TestConfig::max_version },
+ { "-expect-version", &TestConfig::expect_version },
{ "-mtu", &TestConfig::mtu },
{ "-export-keying-material", &TestConfig::export_keying_material },
{ "-expect-total-renegotiations", &TestConfig::expect_total_renegotiations },
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 8bc8892e..7b4b3427 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -24,6 +24,7 @@ struct TestConfig {
bool is_server = false;
bool is_dtls = false;
int resume_count = 0;
+ std::string write_settings;
bool fallback_scsv = false;
std::string digest_prefs;
std::vector<int> signing_prefs;
@@ -68,6 +69,7 @@ struct TestConfig {
std::string expected_signed_cert_timestamps;
int min_version = 0;
int max_version = 0;
+ int expect_version = 0;
int mtu = 0;
bool implicit_handshake = false;
bool use_early_callback = false;
@@ -100,6 +102,7 @@ struct TestConfig {
bool shim_shuts_down = false;
bool verify_fail = false;
bool verify_peer = false;
+ bool verify_peer_if_no_obc = false;
bool expect_verify_result = false;
std::string signed_cert_timestamps;
int expect_total_renegotiations = 0;
diff --git a/src/ssl/tls13_enc.c b/src/ssl/tls13_enc.c
index 2137fe25..97f0ed93 100644
--- a/src/ssl/tls13_enc.c
+++ b/src/ssl/tls13_enc.c
@@ -54,15 +54,8 @@ int tls13_init_key_schedule(SSL_HANDSHAKE *hs) {
int tls13_init_early_key_schedule(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
- uint16_t session_version;
- if (!ssl->method->version_from_wire(&session_version,
- ssl->session->ssl_version) ||
- !init_key_schedule(hs, session_version,
- ssl->session->cipher->algorithm_prf)) {
- return 0;
- }
-
- return 1;
+ return init_key_schedule(hs, SSL_SESSION_protocol_version(ssl->session),
+ ssl->session->cipher->algorithm_prf);
}
int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
@@ -122,11 +115,7 @@ int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
const uint8_t *traffic_secret,
size_t traffic_secret_len) {
const SSL_SESSION *session = SSL_get_session(ssl);
- uint16_t version;
- if (!ssl->method->version_from_wire(&version, session->ssl_version)) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
+ uint16_t version = SSL_SESSION_protocol_version(session);
if (traffic_secret_len > 0xff) {
OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
@@ -141,8 +130,7 @@ int tls13_set_traffic_key(SSL *ssl, enum evp_aead_direction_t direction,
return 0;
}
- const EVP_MD *digest = ssl_get_handshake_digest(
- session->cipher->algorithm_prf, version);
+ const EVP_MD *digest = SSL_SESSION_get_digest(session);
/* Derive the key. */
size_t key_len = EVP_AEAD_key_length(aead);
@@ -383,11 +371,7 @@ static int tls13_psk_binder(uint8_t *out, const EVP_MD *digest, uint8_t *psk,
int tls13_write_psk_binder(SSL_HANDSHAKE *hs, uint8_t *msg, size_t len) {
SSL *const ssl = hs->ssl;
- const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session, ssl);
- if (digest == NULL) {
- OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
- return 0;
- }
+ const EVP_MD *digest = SSL_SESSION_get_digest(ssl->session);
size_t hash_len = EVP_MD_size(digest);
if (len < hash_len + 3) {
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 0a5e1a2b..fe2463bc 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -537,11 +537,14 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
goto err;
}
- /* Determine whether to request a client certificate. */
- hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
- /* CertificateRequest may only be sent in non-resumption handshakes. */
- if (ssl->s3->session_reused) {
- hs->cert_request = 0;
+ if (!ssl->s3->session_reused) {
+ /* Determine whether to request a client certificate. */
+ hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
+ /* Only request a certificate if Channel ID isn't negotiated. */
+ if ((ssl->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
+ ssl->s3->tlsext_channel_id_valid) {
+ hs->cert_request = 0;
+ }
}
/* Send a CertificateRequest, if necessary. */
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index 6144f86c..d039b7db 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -65,39 +65,6 @@
#include "internal.h"
-static int ssl3_version_from_wire(uint16_t *out_version,
- uint16_t wire_version) {
- switch (wire_version) {
- case SSL3_VERSION:
- case TLS1_VERSION:
- case TLS1_1_VERSION:
- case TLS1_2_VERSION:
- *out_version = wire_version;
- return 1;
- case TLS1_3_DRAFT_VERSION:
- *out_version = TLS1_3_VERSION;
- return 1;
- }
-
- return 0;
-}
-
-static uint16_t ssl3_version_to_wire(uint16_t version) {
- switch (version) {
- case SSL3_VERSION:
- case TLS1_VERSION:
- case TLS1_1_VERSION:
- case TLS1_2_VERSION:
- return version;
- case TLS1_3_VERSION:
- return TLS1_3_DRAFT_VERSION;
- }
-
- /* It is an error to use this function with an invalid version. */
- assert(0);
- return 0;
-}
-
static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; }
static void ssl3_expect_flight(SSL *ssl) {}
@@ -130,10 +97,6 @@ static int ssl3_set_write_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx) {
static const SSL_PROTOCOL_METHOD kTLSProtocolMethod = {
0 /* is_dtls */,
- SSL3_VERSION,
- TLS1_3_VERSION,
- ssl3_version_from_wire,
- ssl3_version_to_wire,
ssl3_new,
ssl3_free,
ssl3_get_message,
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index a5bbe933..3bc0b297 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -389,8 +389,10 @@ static int do_seal_record(SSL *ssl, uint8_t *out, size_t *out_len,
* ClientHellos should use SSL 3.0 and pre-TLS-1.3 expects the version
* to change after version negotiation. */
uint16_t wire_version = TLS1_VERSION;
- if (ssl->version == SSL3_VERSION ||
- (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION)) {
+ if (ssl->s3->hs != NULL && ssl->s3->hs->max_version == SSL3_VERSION) {
+ wire_version = SSL3_VERSION;
+ }
+ if (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION) {
wire_version = ssl->version;
}
diff --git a/src/tool/client.cc b/src/tool/client.cc
index c4a071de..74a7124c 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -127,6 +127,10 @@ static const struct argument kArguments[] = {
"-ed25519", kBooleanArgument, "Advertise Ed25519 support",
},
{
+ "-http-tunnel", kOptionalArgument,
+ "An HTTP proxy server to tunnel the TCP connection through",
+ },
+ {
"", kOptionalArgument, "",
},
};
@@ -208,7 +212,12 @@ static bool DoConnection(SSL_CTX *ctx,
std::map<std::string, std::string> args_map,
bool (*cb)(SSL *ssl, int sock)) {
int sock = -1;
- if (!Connect(&sock, args_map["-connect"])) {
+ if (args_map.count("-http-tunnel") != 0) {
+ if (!Connect(&sock, args_map["-http-tunnel"]) ||
+ !DoHTTPTunnel(sock, args_map["-connect"])) {
+ return false;
+ }
+ } else if (!Connect(&sock, args_map["-connect"])) {
return false;
}
diff --git a/src/tool/transport_common.cc b/src/tool/transport_common.cc
index 912e6802..0fc7c3c1 100644
--- a/src/tool/transport_common.cc
+++ b/src/tool/transport_common.cc
@@ -70,10 +70,8 @@ bool InitSocketLibrary() {
return true;
}
-// Connect sets |*out_sock| to be a socket connected to the destination given
-// in |hostname_and_port|, which should be of the form "www.example.com:123".
-// It returns true on success and false otherwise.
-bool Connect(int *out_sock, const std::string &hostname_and_port) {
+static void SplitHostPort(std::string *out_hostname, std::string *out_port,
+ const std::string &hostname_and_port) {
size_t colon_offset = hostname_and_port.find_last_of(':');
const size_t bracket_offset = hostname_and_port.find_last_of(']');
std::string hostname, port;
@@ -85,12 +83,20 @@ bool Connect(int *out_sock, const std::string &hostname_and_port) {
}
if (colon_offset == std::string::npos) {
- hostname = hostname_and_port;
- port = "443";
+ *out_hostname = hostname_and_port;
+ *out_port = "443";
} else {
- hostname = hostname_and_port.substr(0, colon_offset);
- port = hostname_and_port.substr(colon_offset + 1);
+ *out_hostname = hostname_and_port.substr(0, colon_offset);
+ *out_port = hostname_and_port.substr(colon_offset + 1);
}
+}
+
+// Connect sets |*out_sock| to be a socket connected to the destination given
+// in |hostname_and_port|, which should be of the form "www.example.com:123".
+// It returns true on success and false otherwise.
+bool Connect(int *out_sock, const std::string &hostname_and_port) {
+ std::string hostname, port;
+ SplitHostPort(&hostname, &port, hostname_and_port);
// Handle IPv6 literals.
if (hostname.size() >= 2 && hostname[0] == '[' &&
@@ -631,3 +637,31 @@ bool DoSMTPStartTLS(int sock) {
return true;
}
+
+bool DoHTTPTunnel(int sock, const std::string &hostname_and_port) {
+ std::string hostname, port;
+ SplitHostPort(&hostname, &port, hostname_and_port);
+
+ fprintf(stderr, "Establishing HTTP tunnel to %s:%s.\n", hostname.c_str(),
+ port.c_str());
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "CONNECT %s:%s HTTP/1.0\r\n\r\n", hostname.c_str(),
+ port.c_str());
+ if (!SendAll(sock, buf, strlen(buf))) {
+ return false;
+ }
+
+ SocketLineReader line_reader(sock);
+
+ // Read until an empty line, signaling the end of the HTTP response.
+ std::string line;
+ for (;;) {
+ if (!line_reader.Next(&line)) {
+ return false;
+ }
+ if (line.empty()) {
+ return true;
+ }
+ fprintf(stderr, "%s\n", line.c_str());
+ }
+}
diff --git a/src/tool/transport_common.h b/src/tool/transport_common.h
index 7c157ba0..b1496716 100644
--- a/src/tool/transport_common.h
+++ b/src/tool/transport_common.h
@@ -18,6 +18,8 @@
#include <openssl/ssl.h>
#include <string.h>
+#include <string>
+
// InitSocketLibrary calls the Windows socket init functions, if needed.
bool InitSocketLibrary();
@@ -59,4 +61,8 @@ bool TransferData(SSL *ssl, int sock);
// returns true on success and false otherwise.
bool DoSMTPStartTLS(int sock);
+// DoHTTPTunnel sends an HTTP CONNECT request over |sock|. It returns true on
+// success and false otherwise.
+bool DoHTTPTunnel(int sock, const std::string &hostname_and_port);
+
#endif /* !OPENSSL_HEADER_TOOL_TRANSPORT_COMMON_H */