From 025b3d3459260670a883169cf0940e94cbd79b94 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Tue, 1 Jul 2014 19:53:04 -0400 Subject: [PATCH] Add some basic server tests to runner.go. client_shim.cc and runner.go are generalized to handle both ends. Plumb a bit through the test case to control which and add server versions of all the cipher suite tests. Change-Id: Iab2640b390f7ed7160c9a9bf6bb34b8bec761b2e Reviewed-on: https://boringssl-review.googlesource.com/1091 Reviewed-by: Adam Langley --- ssl/test/CMakeLists.txt | 6 +- ssl/test/bssl_shim.cc | 164 ++++++++++++++++++++++++++++++++++++++ ssl/test/client_shim.cc | 118 --------------------------- ssl/test/runner/runner.go | 137 ++++++++++++++++++++++++------- 4 files changed, 274 insertions(+), 151 deletions(-) create mode 100644 ssl/test/bssl_shim.cc delete mode 100644 ssl/test/client_shim.cc diff --git a/ssl/test/CMakeLists.txt b/ssl/test/CMakeLists.txt index 7d073c1c..1b70e99e 100644 --- a/ssl/test/CMakeLists.txt +++ b/ssl/test/CMakeLists.txt @@ -1,9 +1,9 @@ include_directories(../../include) add_executable( - client_shim + bssl_shim - client_shim.cc + bssl_shim.cc ) -target_link_libraries(client_shim ssl crypto) +target_link_libraries(bssl_shim ssl crypto) diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc new file mode 100644 index 00000000..e8cca129 --- /dev/null +++ b/ssl/test/bssl_shim.cc @@ -0,0 +1,164 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include +#include +#include +#include + +#include +#include + + +SSL *setup_test(int is_server) { + if (!SSL_library_init()) { + return NULL; + } + + SSL_CTX *ssl_ctx = NULL; + SSL *ssl = NULL; + BIO *bio = NULL; + + ssl_ctx = SSL_CTX_new( + is_server ? SSLv23_server_method() : SSLv23_client_method()); + if (ssl_ctx == NULL) { + goto err; + } + + if (!SSL_CTX_set_ecdh_auto(ssl_ctx, 1)) { + goto err; + } + + if (!SSL_CTX_set_cipher_list(ssl_ctx, "ALL")) { + goto err; + } + + ssl = SSL_new(ssl_ctx); + if (ssl == NULL) { + goto err; + } + + bio = BIO_new_fd(3, 1 /* take ownership */); + if (bio == NULL) { + goto err; + } + + SSL_set_bio(ssl, bio, bio); + SSL_CTX_free(ssl_ctx); + + return ssl; + +err: + if (bio != NULL) { + BIO_free(bio); + } + if (ssl != NULL) { + SSL_free(ssl); + } + if (ssl_ctx != NULL) { + SSL_CTX_free(ssl_ctx); + } + return NULL; +} + +int main(int argc, char **argv) { + int i, is_server, ret; + + if (argc < 2) { + fprintf(stderr, "Usage: %s (client|server) [flags...]\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "client") == 0) { + is_server = 0; + } else if (strcmp(argv[1], "server") == 0) { + is_server = 1; + } else { + fprintf(stderr, "Usage: %s (client|server) [flags...]\n", argv[0]); + return 1; + } + + SSL *ssl = setup_test(is_server); + if (ssl == NULL) { + BIO_print_errors_fp(stdout); + return 1; + } + + for (i = 2; i < argc; i++) { + if (strcmp(argv[i], "-fallback-scsv") == 0) { + if (!SSL_enable_fallback_scsv(ssl)) { + BIO_print_errors_fp(stdout); + return 1; + } + } else if (strcmp(argv[i], "-key-file") == 0) { + i++; + if (i >= argc) { + fprintf(stderr, "Missing parameter\n"); + return 1; + } + if (!SSL_use_PrivateKey_file(ssl, argv[i], SSL_FILETYPE_PEM)) { + BIO_print_errors_fp(stdout); + return 1; + } + } else if (strcmp(argv[i], "-cert-file") == 0) { + i++; + if (i >= argc) { + fprintf(stderr, "Missing parameter\n"); + return 1; + } + if (!SSL_use_certificate_file(ssl, argv[i], SSL_FILETYPE_PEM)) { + BIO_print_errors_fp(stdout); + return 1; + } + } else { + fprintf(stderr, "Unknown argument: %s\n", argv[i]); + return 1; + } + } + + if (is_server) { + ret = SSL_accept(ssl); + } else { + ret = SSL_connect(ssl); + } + if (ret != 1) { + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 2; + } + + for (;;) { + uint8_t buf[512]; + int n = SSL_read(ssl, buf, sizeof(buf)); + if (n < 0) { + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 3; + } else if (n == 0) { + break; + } else { + for (int i = 0; i < n; i++) { + buf[i] ^= 0xff; + } + int w = SSL_write(ssl, buf, n); + if (w != n) { + SSL_free(ssl); + BIO_print_errors_fp(stdout); + return 4; + } + } + } + + SSL_free(ssl); + return 0; +} diff --git a/ssl/test/client_shim.cc b/ssl/test/client_shim.cc deleted file mode 100644 index 0087a77e..00000000 --- a/ssl/test/client_shim.cc +++ /dev/null @@ -1,118 +0,0 @@ -/* Copyright (c) 2014, Google Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ - -#include -#include -#include -#include - -#include -#include - - -SSL *setup_test() { - if (!SSL_library_init()) { - return NULL; - } - - SSL_CTX *client_ctx = NULL; - SSL *client = NULL; - BIO *bio = NULL; - - client_ctx = SSL_CTX_new(SSLv23_client_method()); - if (client_ctx == NULL) { - goto err; - } - - if (!SSL_CTX_set_cipher_list(client_ctx, "ALL")) { - goto err; - } - - client = SSL_new(client_ctx); - if (client == NULL) { - goto err; - } - - bio = BIO_new_fd(3, 1 /* take ownership */); - if (bio == NULL) { - goto err; - } - - SSL_set_bio(client, bio, bio); - SSL_CTX_free(client_ctx); - - return client; - -err: - if (bio != NULL) { - BIO_free(bio); - } - if (client != NULL) { - SSL_free(client); - } - if (client_ctx != NULL) { - SSL_CTX_free(client_ctx); - } - return NULL; -} - -int main(int argc, char **argv) { - int i; - - SSL *client = setup_test(); - if (client == NULL) { - BIO_print_errors_fp(stdout); - return 1; - } - - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-fallback-scsv") == 0) { - SSL_enable_fallback_scsv(client); - } else { - fprintf(stderr, "Unknown argument: %s\n", argv[i]); - return 1; - } - } - - if (SSL_connect(client) != 1) { - SSL_free(client); - BIO_print_errors_fp(stdout); - return 2; - } - - for (;;) { - uint8_t buf[512]; - int n = SSL_read(client, buf, sizeof(buf)); - if (n < 0) { - SSL_free(client); - BIO_print_errors_fp(stdout); - return 3; - } else if (n == 0) { - break; - } else { - for (int i = 0; i < n; i++) { - buf[i] ^= 0xff; - } - int w = SSL_write(client, buf, n); - if (w != n) { - SSL_free(client); - BIO_print_errors_fp(stdout); - return 4; - } - } - } - - SSL_free(client); - return 0; -} diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 1b86a959..2cda6605 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go @@ -15,16 +15,26 @@ import ( var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind") +const ( + rsaCertificateFile = "cert.pem" + ecdsaCertificateFile = "ecdsa_cert.pem" +) + +const ( + rsaKeyFile = "key.pem" + ecdsaKeyFile = "ecdsa_key.pem" +) + var rsaCertificate, ecdsaCertificate Certificate func initCertificates() { var err error - rsaCertificate, err = LoadX509KeyPair("cert.pem", "key.pem") + rsaCertificate, err = LoadX509KeyPair(rsaCertificateFile, rsaKeyFile) if err != nil { panic(err) } - ecdsaCertificate, err = LoadX509KeyPair("ecdsa_cert.pem", "ecdsa_key.pem") + ecdsaCertificate, err = LoadX509KeyPair(ecdsaCertificateFile, ecdsaKeyFile) if err != nil { panic(err) } @@ -42,7 +52,15 @@ func getECDSACertificate() Certificate { return ecdsaCertificate } +type testType int + +const ( + clientTest testType = iota + serverTest +) + type testCase struct { + testType testType name string config Config shouldFail bool @@ -53,12 +71,16 @@ type testCase struct { // messageLen is the length, in bytes, of the test message that will be // sent. messageLen int + // certFile is the path to the certificate to use for the server. + certFile string + // keyFile is the path to the private key to use for the server. + keyFile string // flags, if not empty, contains a list of command-line flags that will // be passed to the shim program. flags []string } -var clientTests = []testCase{ +var testCases = []testCase{ { name: "BadRSASignature", config: Config{ @@ -170,7 +192,7 @@ func runTest(test *testCase) error { syscall.CloseOnExec(socks[0]) syscall.CloseOnExec(socks[1]) - clientEnd := os.NewFile(uintptr(socks[0]), "client end") + shimEnd := os.NewFile(uintptr(socks[0]), "shim end") connFile := os.NewFile(uintptr(socks[1]), "our end") conn, err := net.FileConn(connFile) connFile.Close() @@ -178,35 +200,63 @@ func runTest(test *testCase) error { panic(err) } - const shim_path = "../../../build/ssl/test/client_shim" - var client *exec.Cmd + const shim_path = "../../../build/ssl/test/bssl_shim" + flags := []string{} + if test.testType == clientTest { + flags = append(flags, "client") + } else { + flags = append(flags, "server") + + flags = append(flags, "-key-file") + if test.keyFile == "" { + flags = append(flags, rsaKeyFile) + } else { + flags = append(flags, test.keyFile) + } + + flags = append(flags, "-cert-file") + if test.certFile == "" { + flags = append(flags, rsaCertificateFile) + } else { + flags = append(flags, test.certFile) + } + } + flags = append(flags, test.flags...) + + var shim *exec.Cmd if *useValgrind { - client = valgrindOf(false, shim_path, test.flags...) + shim = valgrindOf(false, shim_path, flags...) } else { - client = exec.Command(shim_path, test.flags...) + shim = exec.Command(shim_path, flags...) } - //client := gdbOf(shim_path) - client.ExtraFiles = []*os.File{clientEnd} - client.Stdin = os.Stdin + // shim = gdbOf(shim_path, flags...) + shim.ExtraFiles = []*os.File{shimEnd} + shim.Stdin = os.Stdin var stdoutBuf, stderrBuf bytes.Buffer - client.Stdout = &stdoutBuf - client.Stderr = &stderrBuf + shim.Stdout = &stdoutBuf + shim.Stderr = &stderrBuf - if err := client.Start(); err != nil { + if err := shim.Start(); err != nil { panic(err) } - clientEnd.Close() + shimEnd.Close() config := test.config - if len(config.Certificates) == 0 { - config.Certificates = []Certificate{getRSACertificate()} - } - tlsConn := Server(conn, &config) + var tlsConn *Conn + if test.testType == clientTest { + if len(config.Certificates) == 0 { + config.Certificates = []Certificate{getRSACertificate()} + } + tlsConn = Server(conn, &config) + } else { + config.InsecureSkipVerify = true + tlsConn = Client(conn, &config) + } err = doExchange(tlsConn, test.messageLen) conn.Close() - childErr := client.Wait() + childErr := shim.Wait() stdout := string(stdoutBuf.Bytes()) stderr := string(stderrBuf.Bytes()) @@ -282,10 +332,16 @@ var testCipherSuites = []struct { func addCipherSuiteTests() { for _, suite := range testCipherSuites { var cert Certificate + var certFile string + var keyFile string if strings.Contains(suite.name, "ECDSA") { cert = getECDSACertificate() + certFile = ecdsaCertificateFile + keyFile = ecdsaKeyFile } else { cert = getRSACertificate() + certFile = rsaCertificateFile + keyFile = rsaKeyFile } for _, ver := range tlsVersions { @@ -293,8 +349,9 @@ func addCipherSuiteTests() { continue } - clientTests = append(clientTests, testCase{ - name: ver.name + "-" + suite.name, + testCases = append(testCases, testCase{ + testType: clientTest, + name: ver.name + "-" + suite.name + "-client", config: Config{ MinVersion: ver.version, MaxVersion: ver.version, @@ -302,6 +359,26 @@ func addCipherSuiteTests() { Certificates: []Certificate{cert}, }, }) + + // Go's TLS implementation implements SSLv3 as a server, + // but not as a client. + // + // TODO(davidben): Implement SSLv3 as a client too to + // exercise that code. + if ver.version != VersionSSL30 { + testCases = append(testCases, testCase{ + testType: serverTest, + name: ver.name + "-" + suite.name + "-server", + config: Config{ + MinVersion: ver.version, + MaxVersion: ver.version, + CipherSuites: []uint16{suite.id}, + Certificates: []Certificate{cert}, + }, + certFile: certFile, + keyFile: keyFile, + }) + } } } } @@ -309,7 +386,7 @@ func addCipherSuiteTests() { func addBadECDSASignatureTests() { for badR := BadValue(1); badR < NumBadValues; badR++ { for badS := BadValue(1); badS < NumBadValues; badS++ { - clientTests = append(clientTests, testCase{ + testCases = append(testCases, testCase{ name: fmt.Sprintf("BadECDSA-%d-%d", badR, badS), config: Config{ CipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, @@ -327,7 +404,7 @@ func addBadECDSASignatureTests() { } func addCBCPaddingTests() { - clientTests = append(clientTests, testCase{ + testCases = append(testCases, testCase{ name: "MaxCBCPadding", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, @@ -337,7 +414,7 @@ func addCBCPaddingTests() { }, messageLen: 12, // 20 bytes of SHA-1 + 12 == 0 % block size }) - clientTests = append(clientTests, testCase{ + testCases = append(testCases, testCase{ name: "BadCBCPadding", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, @@ -350,7 +427,7 @@ func addCBCPaddingTests() { }) // OpenSSL previously had an issue where the first byte of padding in // 255 bytes of padding wasn't checked. - clientTests = append(clientTests, testCase{ + testCases = append(testCases, testCase{ name: "BadCBCPadding255", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, @@ -421,16 +498,16 @@ func main() { testChan := make(chan *testCase, numWorkers) doneChan := make(chan struct{}) - go statusPrinter(doneChan, statusChan, len(clientTests)) + go statusPrinter(doneChan, statusChan, len(testCases)) for i := 0; i < numWorkers; i++ { wg.Add(1) go worker(statusChan, testChan, &wg) } - for i := range clientTests { - if len(*flagTest) == 0 || *flagTest == clientTests[i].name { - testChan <- &clientTests[i] + for i := range testCases { + if len(*flagTest) == 0 || *flagTest == testCases[i].name { + testChan <- &testCases[i] } }