summaryrefslogtreecommitdiff
path: root/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
diff options
context:
space:
mode:
Diffstat (limited to 'nss/gtests/nss_bogo_shim/nss_bogo_shim.cc')
-rw-r--r--nss/gtests/nss_bogo_shim/nss_bogo_shim.cc254
1 files changed, 204 insertions, 50 deletions
diff --git a/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc b/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
index a128cbb..e12714e 100644
--- a/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
+++ b/nss/gtests/nss_bogo_shim/nss_bogo_shim.cc
@@ -12,19 +12,16 @@
#include "nss.h"
#include "prio.h"
#include "prnetdb.h"
+#include "secerr.h"
#include "ssl.h"
+#include "ssl3prot.h"
#include "sslerr.h"
#include "sslproto.h"
#include "nsskeys.h"
-static const char* kVersionDisableFlags[] = {
- "no-ssl3",
- "no-tls1",
- "no-tls11",
- "no-tls12",
- "no-tls13"
-};
+static const char* kVersionDisableFlags[] = {"no-ssl3", "no-tls1", "no-tls11",
+ "no-tls12", "no-tls13"};
bool exitCodeUnimplemented = false;
@@ -119,16 +116,17 @@ class TestAgent {
if (cfg_.get<std::string>("key-file") != "") {
key_ = ReadPrivateKey(cfg_.get<std::string>("key-file"));
- if (!key_) {
- // Temporary to handle our inability to handle ECDSA.
- exitCodeUnimplemented = true;
- return false;
- }
+ if (!key_) return false;
}
if (cfg_.get<std::string>("cert-file") != "") {
cert_ = ReadCertificate(cfg_.get<std::string>("cert-file"));
if (!cert_) return false;
}
+
+ // Needed because certs are not entirely valid.
+ rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook, this);
+ if (rv != SECSuccess) return false;
+
if (cfg_.get<bool>("server")) {
// Server
rv = SSL_ConfigServerCert(ssl_fd_, cert_, key_, nullptr, 0);
@@ -136,19 +134,54 @@ class TestAgent {
std::cerr << "Couldn't configure server cert\n";
return false;
}
- } else {
- // Client.
- // Needed because server certs are not entirely valid.
- rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook, this);
+ } else if (key_ && cert_) {
+ // Client.
+ rv = SSL_GetClientAuthDataHook(ssl_fd_, GetClientAuthDataHook, this);
if (rv != SECSuccess) return false;
+ }
- if (key_ && cert_) {
- rv = SSL_GetClientAuthDataHook(ssl_fd_, GetClientAuthDataHook, this);
- if (rv != SECSuccess) return false;
- }
+ return true;
+ }
+
+ static bool ConvertFromWireVersion(SSLProtocolVariant variant,
+ int wire_version, uint16_t* lib_version) {
+ // These default values are used when {min,max}-version isn't given.
+ if (wire_version == 0 || wire_version == 0xffff) {
+ *lib_version = static_cast<uint16_t>(wire_version);
+ return true;
+ }
+
+#ifdef TLS_1_3_DRAFT_VERSION
+ if (wire_version == (0x7f00 | TLS_1_3_DRAFT_VERSION)) {
+ // N.B. SSL_LIBRARY_VERSION_DTLS_1_3_WIRE == SSL_LIBRARY_VERSION_TLS_1_3
+ wire_version = SSL_LIBRARY_VERSION_TLS_1_3;
}
+#endif
+ if (variant == ssl_variant_datagram) {
+ switch (wire_version) {
+ case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
+ *lib_version = SSL_LIBRARY_VERSION_DTLS_1_0;
+ break;
+ case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
+ *lib_version = SSL_LIBRARY_VERSION_DTLS_1_2;
+ break;
+ case SSL_LIBRARY_VERSION_DTLS_1_3_WIRE:
+ *lib_version = SSL_LIBRARY_VERSION_DTLS_1_3;
+ break;
+ default:
+ std::cerr << "Unrecognized DTLS version " << wire_version << ".\n";
+ return false;
+ }
+ } else {
+ if (wire_version < SSL_LIBRARY_VERSION_3_0 ||
+ wire_version > SSL_LIBRARY_VERSION_TLS_1_3) {
+ std::cerr << "Unrecognized TLS version " << wire_version << ".\n";
+ return false;
+ }
+ *lib_version = static_cast<uint16_t>(wire_version);
+ }
return true;
}
@@ -158,27 +191,18 @@ class TestAgent {
return false;
}
- auto max_allowed = static_cast<uint16_t>(cfg_.get<int>("max-version"));
- if (variant == ssl_variant_datagram) {
- // For DTLS this is the wire version; adjust if needed.
- switch (max_allowed) {
- case SSL_LIBRARY_VERSION_DTLS_1_0_WIRE:
- max_allowed = SSL_LIBRARY_VERSION_DTLS_1_0;
- break;
- case SSL_LIBRARY_VERSION_DTLS_1_2_WIRE:
- max_allowed = SSL_LIBRARY_VERSION_DTLS_1_2;
- break;
- case SSL_LIBRARY_VERSION_DTLS_1_3_WIRE:
- max_allowed = SSL_LIBRARY_VERSION_DTLS_1_3;
- break;
- case 0xffff: // No maximum specified.
- break;
- default:
- // Unrecognized DTLS version.
- return false;
- }
+ uint16_t min_allowed;
+ uint16_t max_allowed;
+ if (!ConvertFromWireVersion(variant, cfg_.get<int>("min-version"),
+ &min_allowed)) {
+ return false;
+ }
+ if (!ConvertFromWireVersion(variant, cfg_.get<int>("max-version"),
+ &max_allowed)) {
+ return false;
}
+ min_allowed = std::max(min_allowed, supported.min);
max_allowed = std::min(max_allowed, supported.max);
bool found_min = false;
@@ -186,7 +210,7 @@ class TestAgent {
// Ignore -no-ssl3, because SSLv3 is never supported.
for (size_t i = 1; i < PR_ARRAY_SIZE(kVersionDisableFlags); ++i) {
auto version =
- static_cast<uint16_t>(SSL_LIBRARY_VERSION_TLS_1_0 + (i - 1));
+ static_cast<uint16_t>(SSL_LIBRARY_VERSION_TLS_1_0 + (i - 1));
if (variant == ssl_variant_datagram) {
// In DTLS mode, the -no-tlsN flags refer to DTLS versions,
// but NSS wants the corresponding TLS versions.
@@ -199,7 +223,7 @@ class TestAgent {
}
}
- if (version < supported.min) {
+ if (version < min_allowed) {
continue;
}
if (version > max_allowed) {
@@ -220,12 +244,14 @@ class TestAgent {
}
}
if (found_max && allowed) {
- // Discontiguous range.
+ std::cerr << "Discontiguous version range.\n";
return false;
}
}
- // Iff found_min is still false, no usable version was found.
+ if (!found_min) {
+ std::cerr << "All versions disabled.\n";
+ }
return found_min;
}
@@ -239,9 +265,56 @@ class TestAgent {
rv = SSL_VersionRangeSet(ssl_fd_, &vrange);
if (rv != SECSuccess) return false;
+ SSLVersionRange verify_vrange;
+ rv = SSL_VersionRangeGet(ssl_fd_, &verify_vrange);
+ if (rv != SECSuccess) return false;
+ if (vrange.min != verify_vrange.min || vrange.max != verify_vrange.max)
+ return false;
+
rv = SSL_OptionSet(ssl_fd_, SSL_NO_CACHE, false);
if (rv != SECSuccess) return false;
+ auto alpn = cfg_.get<std::string>("advertise-alpn");
+ if (!alpn.empty()) {
+ assert(!cfg_.get<bool>("server"));
+
+ rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_ALPN, PR_TRUE);
+ if (rv != SECSuccess) return false;
+
+ rv = SSL_SetNextProtoNego(
+ ssl_fd_, reinterpret_cast<const unsigned char*>(alpn.c_str()),
+ alpn.size());
+ if (rv != SECSuccess) return false;
+ }
+
+ if (cfg_.get<bool>("fallback-scsv")) {
+ rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_FALLBACK_SCSV, PR_TRUE);
+ if (rv != SECSuccess) return false;
+ }
+
+ if (cfg_.get<bool>("false-start")) {
+ rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_FALSE_START, PR_TRUE);
+ if (rv != SECSuccess) return false;
+ }
+
+ if (cfg_.get<bool>("enable-ocsp-stapling")) {
+ rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_OCSP_STAPLING, PR_TRUE);
+ if (rv != SECSuccess) return false;
+ }
+
+ bool requireClientCert = cfg_.get<bool>("require-any-client-certificate");
+ if (requireClientCert || cfg_.get<bool>("verify-peer")) {
+ assert(cfg_.get<bool>("server"));
+
+ rv = SSL_OptionSet(ssl_fd_, SSL_REQUEST_CERTIFICATE, PR_TRUE);
+ if (rv != SECSuccess) return false;
+
+ rv = SSL_OptionSet(
+ ssl_fd_, SSL_REQUIRE_CERTIFICATE,
+ requireClientCert ? SSL_REQUIRE_ALWAYS : SSL_REQUIRE_NO_ERROR);
+ if (rv != SECSuccess) return false;
+ }
+
if (!cfg_.get<bool>("server")) {
// Needed to make resumption work.
rv = SSL_SetURL(ssl_fd_, "server");
@@ -312,12 +385,53 @@ class TestAgent {
rv = PR_Write(ssl_fd_, block, len);
if (rv != len) {
std::cerr << "Write failure\n";
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
return SECFailure;
}
}
return SECSuccess;
}
+ // Write bytes to the other side then read them back and check
+ // that they were correctly XORed as in ReadWrite.
+ SECStatus WriteRead() {
+ static const uint8_t ch = 'E';
+
+ // We do 600-byte blocks to provide mis-alignment of the
+ // reader and writer.
+ uint8_t block[600];
+ memset(block, ch, sizeof(block));
+ int32_t rv = PR_Write(ssl_fd_, block, sizeof(block));
+ if (rv != sizeof(block)) {
+ std::cerr << "Write failure\n";
+ PORT_SetError(SEC_ERROR_OUTPUT_LEN);
+ return SECFailure;
+ }
+
+ size_t left = sizeof(block);
+ while (left) {
+ int32_t rv = PR_Read(ssl_fd_, block, left);
+ if (rv < 0) {
+ std::cerr << "Failure reading\n";
+ return SECFailure;
+ }
+ if (rv == 0) {
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ return SECFailure;
+ }
+
+ int32_t len = rv;
+ for (int32_t i = 0; i < len; ++i) {
+ if (block[i] != (ch ^ 0xff)) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ }
+ left -= len;
+ }
+ return SECSuccess;
+ }
+
SECStatus DoExchange() {
SECStatus rv = Handshake();
if (rv != SECSuccess) {
@@ -327,12 +441,44 @@ class TestAgent {
return SECFailure;
}
- rv = ReadWrite();
- if (rv != SECSuccess) {
- PRErrorCode err = PR_GetError();
- std::cerr << "ReadWrite failed with error=" << FormatError(err)
- << std::endl;
- return SECFailure;
+ if (cfg_.get<bool>("write-then-read")) {
+ rv = WriteRead();
+ if (rv != SECSuccess) {
+ PRErrorCode err = PR_GetError();
+ std::cerr << "WriteRead failed with error=" << FormatError(err)
+ << std::endl;
+ return SECFailure;
+ }
+ } else {
+ rv = ReadWrite();
+ if (rv != SECSuccess) {
+ PRErrorCode err = PR_GetError();
+ std::cerr << "ReadWrite failed with error=" << FormatError(err)
+ << std::endl;
+ return SECFailure;
+ }
+ }
+
+ auto alpn = cfg_.get<std::string>("expect-alpn");
+ if (!alpn.empty()) {
+ SSLNextProtoState state;
+ char chosen[256];
+ unsigned int chosen_len;
+ rv = SSL_GetNextProto(ssl_fd_, &state,
+ reinterpret_cast<unsigned char*>(chosen),
+ &chosen_len, sizeof(chosen));
+ if (rv != SECSuccess) {
+ PRErrorCode err = PR_GetError();
+ std::cerr << "SSL_GetNextProto failed with error=" << FormatError(err)
+ << std::endl;
+ return SECFailure;
+ }
+
+ assert(chosen_len <= sizeof(chosen));
+ if (std::string(chosen, chosen_len) != alpn) {
+ std::cerr << "Unexpected ALPN selection" << std::endl;
+ return SECFailure;
+ }
}
return SECSuccess;
@@ -354,10 +500,19 @@ std::unique_ptr<const Config> ReadConfig(int argc, char** argv) {
cfg->AddEntry<int>("resume-count", 0);
cfg->AddEntry<std::string>("key-file", "");
cfg->AddEntry<std::string>("cert-file", "");
+ cfg->AddEntry<int>("min-version", 0);
cfg->AddEntry<int>("max-version", 0xffff);
for (auto flag : kVersionDisableFlags) {
cfg->AddEntry<bool>(flag, false);
}
+ cfg->AddEntry<bool>("fallback-scsv", false);
+ cfg->AddEntry<bool>("false-start", false);
+ cfg->AddEntry<bool>("enable-ocsp-stapling", false);
+ cfg->AddEntry<bool>("write-then-read", false);
+ cfg->AddEntry<bool>("require-any-client-certificate", false);
+ cfg->AddEntry<bool>("verify-peer", false);
+ cfg->AddEntry<std::string>("advertise-alpn", "");
+ cfg->AddEntry<std::string>("expect-alpn", "");
auto rv = cfg->ParseArgs(argc, argv);
switch (rv) {
@@ -373,7 +528,6 @@ std::unique_ptr<const Config> ReadConfig(int argc, char** argv) {
return std::move(cfg);
}
-
bool RunCycle(std::unique_ptr<const Config>& cfg) {
std::unique_ptr<TestAgent> agent(TestAgent::Create(*cfg));
return agent && agent->DoExchange() == SECSuccess;