From 38ce652a600ca43db4996f3c2945aff39a323e36 Mon Sep 17 00:00:00 2001 From: Vitaly Chikunov Date: Mon, 25 Feb 2019 11:40:19 +0300 Subject: [PATCH] test_tls: simple TLS connection testing Generate cert for every parameter, set it up on s_server, connect s_client, and transfer 1M of data to both directions. --- CMakeLists.txt | 6 + test_tls.c | 357 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 test_tls.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0538af5..f019f14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,11 @@ target_link_libraries(test_sign gost_engine gost_core ${OPENSSL_CRYPTO_LIBRARY}) add_test(NAME sign/verify COMMAND test_sign) +add_executable(test_tls test_tls.c) +target_link_libraries(test_tls gost_engine gost_core ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_SSL_LIBRARY}) +add_test(NAME TLS + COMMAND test_tls) + add_executable(test_context test_context.c) target_link_libraries(test_context gost_engine gost_core ${OPENSSL_CRYPTO_LIBRARY}) add_test(NAME context @@ -191,6 +196,7 @@ set(BINARY_TESTS_TARGETS test_grasshopper test_keyexpimp test_gost89 + test_tls ) set_property(TARGET ${BINARY_TESTS_TARGETS} APPEND PROPERTY COMPILE_DEFINITIONS ENGINE_DIR="${OUTPUT_DIRECTORY}") diff --git a/test_tls.c b/test_tls.c new file mode 100644 index 0000000..6a9c02d --- /dev/null +++ b/test_tls.c @@ -0,0 +1,357 @@ +/* + * Simple Client/Server connection test + * + * Based on OpenSSL example code. + * Copyright (C) 2019 vt@altlinux.org. All Rights Reserved. + * + * Contents licensed under the terms of the OpenSSL license + * See https://www.openssl.org/source/license.html for details + */ + +#include "e_gost_err.h" +#include "gost_lcl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* For X509_NAME_add_entry_by_txt */ +#pragma GCC diagnostic ignored "-Wpointer-sign" + +#define T(e) ({ if (!(e)) { \ + ERR_print_errors_fp(stderr); \ + OpenSSLDie(__FILE__, __LINE__, #e); \ + } \ + }) +#define TE(e) ({ if (!(e)) { \ + ERR_print_errors_fp(stderr); \ + fprintf(stderr, "Error at %s:%d %s\n", __FILE__, __LINE__, #e); \ + return -1; \ + } \ + }) + +#define cRED "\033[1;31m" +#define cDRED "\033[0;31m" +#define cGREEN "\033[1;32m" +#define cDGREEN "\033[0;32m" +#define cBLUE "\033[1;34m" +#define cDBLUE "\033[0;34m" +#define cNORM "\033[m" +#define TEST_ASSERT(e) {if ((test = (e))) \ + printf(cRED " Test FAILED\n" cNORM); \ + else \ + printf(cGREEN " Test passed\n" cNORM);} + +struct certkey { + EVP_PKEY *pkey; + X509 *cert; +}; + +/* How much K to transfer between client and server. */ +#define KTRANSFER (1 * 1024) + +/* + * Simple TLS Server code is based on + * https://wiki.openssl.org/index.php/Simple_TLS_Server + */ +static int s_server(EVP_PKEY *pkey, X509 *cert, int pipewr) +{ + SSL_CTX *ctx; + T(ctx = SSL_CTX_new(TLS_server_method())); + T(SSL_CTX_use_certificate(ctx, cert)); + T(SSL_CTX_use_PrivateKey(ctx, pkey)); + T(SSL_CTX_check_private_key(ctx)); + + struct sockaddr_in addr = { .sin_family = AF_INET }; + socklen_t len; + int sock; + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + err(1, "socket"); + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) + err(1, "setsockopt"); + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) + err(1, "bind"); + len = sizeof(addr); + if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) + err(1, "getsockname"); + int port = ntohs(addr.sin_port); + if (listen(sock, 1) < 0) + err(1, "listen"); + /* Signal to client that server is ready. */ + if (write(pipewr, &port, sizeof(port)) != sizeof(port)) + err(1, "write pipe"); + len = sizeof(addr); + alarm(1); + int client = accept(sock, (struct sockaddr *)&addr, &len); + if (client < 0) + err(1, "accept"); + alarm(0); + SSL *ssl; + ssl = SSL_new(ctx); + SSL_set_fd(ssl, client); + T(SSL_accept(ssl) == 1); + + /* Receive data from client */ + char buf[1024]; + int i; + for (i = 0; i < KTRANSFER; i++) { + int k; + + T(SSL_read(ssl, buf, sizeof(buf)) == sizeof(buf)); + for (k = 0; k < sizeof(buf); k++) + if (buf[k] != 'c') + err(1, "corruption from client"); + } + /* Send data to client. */ + memset(buf, 's', sizeof(buf)); + for (i = 0; i < KTRANSFER; i++) { + T(SSL_write(ssl, buf, sizeof(buf)) == sizeof(buf)); + } + SSL_shutdown(ssl); + SSL_free(ssl); + close(client); + + close(sock); + SSL_CTX_free(ctx); + return 0; +} + +/* + * Simple TLC Client code is based on man BIO_f_ssl and + * https://wiki.openssl.org/index.php/SSL/TLS_Client + */ +static int s_client(int piperd) +{ + SSL_CTX *ctx; + T(ctx = SSL_CTX_new(TLS_client_method())); + + BIO *sbio; + T(sbio = BIO_new_ssl_connect(ctx)); + SSL *ssl; + T(BIO_get_ssl(sbio, &ssl)); + T(SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY)); +#if 0 + /* Does not work with reneg. */ + BIO_set_ssl_renegotiate_bytes(sbio, 100 * 1024); +#endif + int port; + alarm(1); + /* Wait for server to be ready. */ + if (read(piperd, &port, sizeof(port)) != sizeof(port)) + err(1, "read pipe"); + char tport[8]; + snprintf(tport, sizeof(tport), "%d", port); + T(BIO_set_conn_port(sbio, tport)); + T(BIO_do_connect(sbio) == 1); + T(BIO_do_handshake(sbio) == 1); + alarm(0); + + printf("Protocol: %s\n", SSL_get_version(ssl)); + printf("Cipher: %s\n", SSL_get_cipher_name(ssl)); +#if 0 + SSL_SESSION *sess = SSL_get0_session(ssl); + SSL_SESSION_print_fp(stdout, sess); +#endif + + X509 *cert; + T(cert = SSL_get_peer_certificate(ssl)); + X509_free(cert); + int verify = SSL_get_verify_result(ssl); + printf("Verify: %s\n", X509_verify_cert_error_string(verify)); + if (verify != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) + err(1, "invalid SSL_get_verify_result"); + + /* Send data to server. */ + char buf[1024]; + int i; + memset(buf, 'c', sizeof(buf)); + for (i = 0; i < KTRANSFER; i++) { + T(BIO_write(sbio, buf, sizeof(buf)) == sizeof(buf)); + } + (void)BIO_shutdown_wr(sbio); + + /* Receive data from server. */ + for (i = 0; i < KTRANSFER; i++) { + int k; + int n = BIO_read(sbio, buf, sizeof(buf)); + + if (n != sizeof(buf)) { + printf("i:%d BIO_read:%d SSL_get_error:%d\n", i, n, + SSL_get_error(ssl, n)); + ERR_print_errors_fp(stderr); + err(1, "BIO_read"); + } + + for (k = 0; k < sizeof(buf); k++) + if (buf[k] != 's') + err(1, "corruption from server"); + } + + i = BIO_get_num_renegotiates(sbio); + if (i) + printf("Renegs: %d\n", i); + BIO_free_all(sbio); + SSL_CTX_free(ctx); + + return 0; +} + +/* Generate simple cert+key pair. Based on req.c */ +static struct certkey certgen(const char *algname, const char *paramset) +{ + /* Keygen. */ + EVP_PKEY *tkey; + T(tkey = EVP_PKEY_new()); + T(EVP_PKEY_set_type_str(tkey, algname, strlen(algname))); + EVP_PKEY_CTX *ctx; + T(ctx = EVP_PKEY_CTX_new(tkey, NULL)); + T(EVP_PKEY_keygen_init(ctx)); + if (paramset) + T(EVP_PKEY_CTX_ctrl_str(ctx, "paramset", paramset)); + EVP_PKEY *pkey = NULL; + T((EVP_PKEY_keygen(ctx, &pkey)) == 1); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(tkey); + + /* REQ. */ + X509_REQ *req = NULL; + T(req = X509_REQ_new()); + T(X509_REQ_set_version(req, 0L)); + X509_NAME *name; + T(name = X509_NAME_new()); + T(X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, "Test CA", -1, -1, 0)); + T(X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, "Test Key", -1, -1, 0)); + T(X509_REQ_set_subject_name(req, name)); + T(X509_REQ_set_pubkey(req, pkey)); + X509_NAME_free(name); + + /* Cert. */ + X509 *x509ss = NULL; + T(x509ss = X509_new()); + T(X509_set_version(x509ss, 2)); + BIGNUM *brnd = BN_new(); + T(BN_rand(brnd, 20 * 8 - 1, -1, 0)); + T(BN_to_ASN1_INTEGER(brnd, X509_get_serialNumber(x509ss))); + T(X509_set_issuer_name(x509ss, X509_REQ_get_subject_name(req))); + T(X509_gmtime_adj(X509_getm_notBefore(x509ss), 0)); + T(X509_time_adj_ex(X509_getm_notAfter(x509ss), 1, 0, NULL)); + T(X509_set_subject_name(x509ss, X509_REQ_get_subject_name(req))); + T(X509_set_pubkey(x509ss, X509_REQ_get0_pubkey(req))); + X509_REQ_free(req); + BN_free(brnd); + + X509V3_CTX v3ctx; + X509V3_set_ctx_nodb(&v3ctx); + X509V3_set_ctx(&v3ctx, x509ss, x509ss, NULL, NULL, 0); + X509_EXTENSION *ext; + T(ext = X509V3_EXT_conf_nid(NULL, &v3ctx, NID_basic_constraints, "critical,CA:TRUE")); + T(X509_add_ext(x509ss, ext, 0)); + X509_EXTENSION_free(ext); + T(ext = X509V3_EXT_conf_nid(NULL, &v3ctx, NID_subject_key_identifier, "hash")); + T(X509_add_ext(x509ss, ext, 1)); + X509_EXTENSION_free(ext); + T(ext = X509V3_EXT_conf_nid(NULL, &v3ctx, NID_authority_key_identifier, "keyid:always,issuer")); + T(X509_add_ext(x509ss, ext, 2)); + X509_EXTENSION_free(ext); + + EVP_MD_CTX *mctx; + T(mctx = EVP_MD_CTX_new()); + T(EVP_DigestSignInit(mctx, NULL, NULL, NULL, pkey)); + T(X509_sign_ctx(x509ss, mctx)); + EVP_MD_CTX_free(mctx); +#if 0 + /* Print cert in text format. */ + X509_print_fp(stdout, x509ss); +#endif +#if 0 + /* Print cert in PEM format. */ + BIO *out = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT); + PEM_write_bio_X509(out, x509ss); + BIO_free_all(out); +#endif + return (struct certkey){ .pkey = pkey, .cert = x509ss }; +} + +int test(const char *algname, const char *paramset) +{ + int ret = 0; + + printf(cBLUE "Test %s", algname); + if (paramset) + printf(cBLUE ":%s", paramset); + printf(cNORM "\n"); + + struct certkey ck; + ck = certgen(algname, paramset); + + int pipefd[2]; + if (pipe(pipefd)) + err(1, "pipe"); + + pid_t pid = fork(); + if (pid < 0) + err(1, "fork"); + + if (pid > 0) { + int status; + + ret = s_client(pipefd[0]); + wait(&status); + ret |= WIFEXITED(status) && WEXITSTATUS(status); + X509_free(ck.cert); + EVP_PKEY_free(ck.pkey); + } else if (pid == 0) { + ret = s_server(ck.pkey, ck.cert, pipefd[1]); + X509_free(ck.cert); + EVP_PKEY_free(ck.pkey); + exit(ret); + } + + return ret; +} + +int main(int argc, char **argv) +{ + int ret = 0; + + setenv("OPENSSL_ENGINES", ENGINE_DIR, 0); + OPENSSL_add_all_algorithms_conf(); + ERR_load_crypto_strings(); + ENGINE *eng; + T(eng = ENGINE_by_id("gost")); + T(ENGINE_init(eng)); + T(ENGINE_set_default(eng, ENGINE_METHOD_ALL)); + + ret |= test("rsa", NULL); + ret |= test("gost2012_256", "A"); + ret |= test("gost2012_256", "B"); + ret |= test("gost2012_256", "C"); + ret |= test("gost2012_256", "TCA"); + ret |= test("gost2012_512", "A"); + ret |= test("gost2012_512", "B"); + ret |= test("gost2012_512", "C"); + + ENGINE_finish(eng); + ENGINE_free(eng); + + if (ret) + printf(cDRED "= Some tests FAILED!\n" cNORM); + else + printf(cDGREEN "= All tests passed!\n" cNORM); + return ret; +} -- 2.39.5