[Openvpn-devel,07/28] Add unit tests for test_tls_decrypt_lite

Message ID 20220422134038.3801239-8-arne@rfc2549.org
State Accepted
Headers show
Series Stateless three-way handshake and control channel improvements | expand

Commit Message

Arne Schwabe April 22, 2022, 3:40 a.m. UTC
This tests currently the existing functionality of test_tls_decrypt_lite
to check if a reset packet is valid or not.

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
---
 tests/unit_tests/openvpn/Makefile.am |  24 +-
 tests/unit_tests/openvpn/test_pkt.c  | 347 +++++++++++++++++++++++++++
 2 files changed, 370 insertions(+), 1 deletion(-)
 create mode 100644 tests/unit_tests/openvpn/test_pkt.c

Comments

Gert Doering April 25, 2022, 6 a.m. UTC | #1
Acked-by: Gert Doering <gert@greenie.muc.de>

Unit tests are always good :-) - and of course 06/28 is only there
to make this happen.  I have only done a "semi-deep" look into the code,
but it seems to cover the usual cases (good packet, short packet, corrupt
packet for nothing/tls-auth/tls-crypt) which is way more than we have now.

Passes "make distcheck" + "make check with mbedTLS and openssl"

[==========] Running 3 test(s).
[ RUN      ] test_tls_decrypt_lite_none
[       OK ] test_tls_decrypt_lite_none
[ RUN      ] test_tls_decrypt_lite_auth
[       OK ] test_tls_decrypt_lite_auth
[ RUN      ] test_tls_decrypt_lite_crypt
[       OK ] test_tls_decrypt_lite_crypt
[==========] 3 test(s) run.
[  PASSED  ] 3 test(s).
PASS: pkt_testdriver

Your patch has been applied to the master branch.

commit 34694688f43ba1cb5827d921df0b374c916119ce
Author: Arne Schwabe
Date:   Fri Apr 22 15:40:36 2022 +0200

     Add unit tests for test_tls_decrypt_lite

     Signed-off-by: Arne Schwabe <arne@rfc2549.org>
     Acked-by: Gert Doering <gert@greenie.muc.de>
     Message-Id: <20220422134038.3801239-8-arne@rfc2549.org>
     URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg24168.html
     Signed-off-by: Gert Doering <gert@greenie.muc.de>


--
kind regards,

Gert Doering

Patch

diff --git a/tests/unit_tests/openvpn/Makefile.am b/tests/unit_tests/openvpn/Makefile.am
index 4141b53f8..63b53a6ac 100644
--- a/tests/unit_tests/openvpn/Makefile.am
+++ b/tests/unit_tests/openvpn/Makefile.am
@@ -6,7 +6,8 @@  if HAVE_LD_WRAP_SUPPORT
 test_binaries += argv_testdriver buffer_testdriver
 endif
 
-test_binaries += crypto_testdriver packet_id_testdriver auth_token_testdriver ncp_testdriver misc_testdriver
+test_binaries += crypto_testdriver packet_id_testdriver auth_token_testdriver ncp_testdriver misc_testdriver \
+    pkt_testdriver
 if HAVE_LD_WRAP_SUPPORT
 test_binaries += tls_crypt_testdriver
 endif
@@ -62,6 +63,27 @@  packet_id_testdriver_SOURCES = test_packet_id.c mock_msg.c mock_msg.h \
 	$(openvpn_srcdir)/packet_id.c \
 	$(openvpn_srcdir)/platform.c
 
+pkt_testdriver_CFLAGS  = @TEST_CFLAGS@ \
+	-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir)
+pkt_testdriver_LDFLAGS = @TEST_LDFLAGS@
+pkt_testdriver_SOURCES = test_pkt.c mock_msg.c mock_msg.h \
+$(openvpn_srcdir)/argv.c \
+	$(openvpn_srcdir)/base64.c \
+	$(openvpn_srcdir)/buffer.c \
+	$(openvpn_srcdir)/crypto.c \
+	$(openvpn_srcdir)/crypto_mbedtls.c \
+	$(openvpn_srcdir)/crypto_openssl.c \
+	$(openvpn_srcdir)/env_set.c \
+	$(openvpn_srcdir)/otime.c \
+	$(openvpn_srcdir)/packet_id.c \
+	$(openvpn_srcdir)/platform.c \
+	$(openvpn_srcdir)/reliable.c \
+	$(openvpn_srcdir)/run_command.c \
+	$(openvpn_srcdir)/session_id.c \
+	$(openvpn_srcdir)/ssl_pkt.c \
+	$(openvpn_srcdir)/tls_crypt.c
+
+
 tls_crypt_testdriver_CFLAGS  = @TEST_CFLAGS@ \
 	-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir)
 tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
diff --git a/tests/unit_tests/openvpn/test_pkt.c b/tests/unit_tests/openvpn/test_pkt.c
new file mode 100644
index 000000000..022e15d3e
--- /dev/null
+++ b/tests/unit_tests/openvpn/test_pkt.c
@@ -0,0 +1,347 @@ 
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2016-2021 Fox Crypto B.V. <openvpn@foxcrypto.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#elif defined(_MSC_VER)
+#include "config-msvc.h"
+#endif
+
+#include "syshead.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include "crypto.h"
+#include "options.h"
+#include "ssl_backend.h"
+#include "ssl_pkt.h"
+#include "tls_crypt.h"
+
+#include "mock_msg.h"
+#include "mss.h"
+
+int
+parse_line(const char *line, char **p, const int n, const char *file,
+           const int line_num, int msglevel, struct gc_arena *gc)
+{
+    /* Dummy function to get the linker happy, should never be called */
+    assert_true(false);
+    return 0;
+}
+
+const char *
+print_link_socket_actual(const struct link_socket_actual *act, struct gc_arena *gc)
+{
+    return "dummy print_link_socket_actual from unit test";
+}
+
+struct test_pkt_context {
+    struct tls_auth_standalone tas_tls_auth;
+    struct tls_auth_standalone tas_crypt;
+};
+
+const char static_key[] = "<tls-auth>\n"
+                          "-----BEGIN OpenVPN Static key V1-----\n"
+                          "37268ea8f95d7f71f9fb8fc03770c460\n"
+                          "daf714a483d815c013ce0a537efc18f2\n"
+                          "8f4f172669d9e6a413bac6741d8ea054\n"
+                          "00f49b7fd6326470f23798c606bf53d4\n"
+                          "de63ebc64ec59d57ce5d04d5b62e68b5\n"
+                          "3ca6e5354351097fa370446c4d269f18\n"
+                          "7bb6ae54af2dc70ff7317fe2f8754b82\n"
+                          "82aad4202f9fa42c8640245d883e2c54\n"
+                          "a0c1c489a036cf3a8964d8d289c1583b\n"
+                          "9447c262b1da5fd167a5d27bd5ac5143\n"
+                          "17bc2343a31a2efc38dd920d910375f5\n"
+                          "1c2e27f3afd36c49269da079f7ce466e\n"
+                          "bb0f9ad13e9bbb4665974e6bc24b513c\n"
+                          "5700393bf4a3e7f967e2f384069ac8a8\n"
+                          "e78b18b15604993fd16515cce9c0f3e4\n"
+                          "2b4126b999005ade802797b0eeb8b9e6\n"
+                          "-----END OpenVPN Static key V1-----\n"
+                          "</tls-auth>\n";
+
+const uint8_t client_reset_v2_none[] =
+    { 0x38, 0x68, 0x91, 0x92,  0x3f, 0xa3, 0x10, 0x34,
+      0x37, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+const uint8_t client_reset_v2_tls_auth[] =
+    { 0x38, 0xde, 0x69, 0x4c, 0x5c, 0x7b, 0xfb, 0xa2,
+      0x74, 0x93, 0x53, 0x7c, 0x1d, 0xed, 0x4e, 0x78,
+     0x15, 0x29, 0xae, 0x7c, 0xfe, 0x4b, 0x8c, 0x6d,
+     0x6b, 0x2b, 0x51, 0xf0, 0x5a, 0x00, 0x00, 0x00,
+     0x01, 0x61, 0xd3, 0xbf, 0x6c, 0x00, 0x00, 0x00,
+     0x00, 0x00};
+
+const uint8_t client_reset_v2_tls_crypt[] =
+    {0x38, 0xf4, 0x19, 0xcb, 0x12, 0xd1, 0xf9, 0xe4,
+     0x8f, 0x00, 0x00, 0x00, 0x01, 0x61, 0xd3, 0xf8,
+     0xe1, 0x33, 0x02, 0x06, 0xf5, 0x68, 0x02, 0xbe,
+     0x44, 0xfb, 0xed, 0x90, 0x50, 0x64, 0xe3, 0xdb,
+     0x43, 0x41, 0x6b, 0xec, 0x5e, 0x52, 0x67, 0x19,
+     0x46, 0x2b, 0x7e, 0xb9, 0x0c, 0x96, 0xde, 0xfc,
+     0x9b, 0x05, 0xc4, 0x48, 0x79, 0xf7};
+
+/* Valid tls-auth client CONTROL_V1 packet with random server id */
+const uint8_t client_ack_tls_auth_randomid[] = {
+    0x20, 0x14, 0x01, 0x4e, 0xbc, 0x80, 0xc6, 0x14,
+    0x2b, 0x7b, 0xc8, 0x76, 0xfb, 0xc5, 0x2e, 0x27,
+    0xb1, 0xc5, 0x07, 0x35, 0x5b, 0xb6, 0x00, 0x6b,
+    0xae, 0x71, 0xba, 0x4e, 0x38, 0x00, 0x00, 0x00,
+    0x03, 0x61, 0xd3, 0xff, 0x53, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x16, 0x03, 0x01, 0x01, 0x0c, 0x01,
+    0x00, 0x01, 0x08, 0x03, 0x03, 0x8c, 0xaa, 0xac,
+    0x3a, 0x1a, 0x07, 0xbd, 0xe7, 0xb7, 0x50, 0x06,
+    0x9b, 0x94, 0x0c, 0x34, 0x4b, 0x5a, 0x35, 0xca,
+    0xc4, 0x79, 0xbd, 0xc9, 0x09, 0xb0, 0x7b, 0xd9,
+    0xee, 0xbb, 0x7d, 0xe7, 0x25, 0x20, 0x39, 0x38,
+    0xe2, 0x18, 0x33, 0x36, 0x14, 0x9f, 0x34, 0xf0,
+    0x44, 0x59, 0x96, 0x8d, 0x0e, 0xd2, 0x47, 0x76,
+    0x64, 0x88, 0x59, 0xe9, 0x38, 0x03, 0x97, 0x96,
+    0x98, 0x45, 0xfb, 0xf5, 0xff, 0x23, 0x00, 0x32,
+    0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0xc0, 0x2c,
+    0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8,
+    0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e,
+    0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23,
+    0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14,
+    0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33,
+    0x00, 0xff, 0x01, 0x00, 0x00, 0x8d, 0x00, 0x0b,
+    0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a,
+    0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17,
+    0x00, 0x1e, 0x00, 0x19, 0x00, 0x18, 0x00, 0x16,
+    0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0d,
+    0x00, 0x30, 0x00, 0x2e, 0x04, 0x03, 0x05, 0x03,
+    0x06, 0x03, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09,
+    0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05,
+    0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01,
+    0x03, 0x03, 0x02, 0x03, 0x03, 0x01, 0x02, 0x01,
+    0x03, 0x02, 0x02, 0x02, 0x04, 0x02, 0x05, 0x02,
+    0x06, 0x02, 0x00, 0x2b, 0x00, 0x05, 0x04, 0x03,
+    0x04, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x02, 0x01,
+    0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00,
+    0x1d, 0x00, 0x20, 0x0e, 0xc9, 0x7a, 0xff, 0x58,
+    0xdb, 0x56, 0xf6, 0x40, 0xd1, 0xed, 0xdb, 0x91,
+    0x81, 0xd6, 0xef, 0x83, 0x86, 0x8a, 0xb2, 0x3d,
+    0x88, 0x92, 0x3f, 0xd8, 0x51, 0x9c, 0xd6, 0x26,
+    0x56, 0x33, 0x6b};
+
+struct tls_auth_standalone init_tas_auth(int key_direction)
+{
+    struct tls_auth_standalone tas = { 0 };
+
+    tas.tls_wrap.mode = TLS_WRAP_AUTH;
+    /* we ignore packet ids on for the first packet check */
+    tas.tls_wrap.opt.flags |= CO_IGNORE_PACKET_ID;
+
+    struct key_type tls_crypt_kt;
+    init_key_type(&tls_crypt_kt, "none", "SHA1", true, false);
+
+    crypto_read_openvpn_key(&tls_crypt_kt, &tas.tls_wrap.opt.key_ctx_bi,
+                            static_key, true, key_direction,
+                            "Control Channel Authentication", "tls-auth");
+    return tas;
+}
+
+struct tls_auth_standalone init_tas_crypt(bool server)
+{
+    struct tls_auth_standalone tas = { 0 };
+    tas.tls_wrap.mode = TLS_WRAP_CRYPT;
+    tas.tls_wrap.opt.flags |= CO_IGNORE_PACKET_ID;
+
+    tls_crypt_init_key(&tas.tls_wrap.opt.key_ctx_bi, static_key, true, server);
+
+    return tas;
+}
+
+void
+test_tls_decrypt_lite_crypt(void **ut_state)
+{
+    struct link_socket_actual from = { 0 };
+    struct tls_pre_decrypt_state state = { 0 };
+
+    struct tls_auth_standalone tas = init_tas_crypt(true);
+
+    struct buffer buf = alloc_buf(1024);
+
+    /* tls-auth should be invalid */
+    buf_write(&buf, client_reset_v2_tls_auth, sizeof(client_reset_v2_tls_auth));
+    enum first_packet_verdict verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_INVALID);
+    free_tls_pre_decrypt_state(&state);
+
+    /* as well as the too short normal reset */
+    buf_reset_len(&buf);
+    buf_write(&buf, client_reset_v2_none, sizeof(client_reset_v2_none));
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_INVALID);
+    free_tls_pre_decrypt_state(&state);
+
+    /* the tls-crypt should validate */
+    buf_reset_len(&buf);
+    buf_write(&buf, client_reset_v2_tls_crypt, sizeof(client_reset_v2_tls_crypt));
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_VALID_RESET);
+    free_tls_pre_decrypt_state(&state);
+
+    /* flip a byte in various places */
+    for (int i=0;i<sizeof(client_reset_v2_tls_crypt);i++)
+    {
+        buf_reset_len(&buf);
+        buf_write(&buf, client_reset_v2_tls_crypt, sizeof(client_reset_v2_tls_crypt));
+        BPTR(&buf)[i] = 0x23;
+        verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+        assert_int_equal(verdict, VERDICT_INVALID);
+        free_tls_pre_decrypt_state(&state);
+    }
+
+    free_key_ctx_bi(&tas.tls_wrap.opt.key_ctx_bi);
+    free_buf(&buf);
+}
+
+
+void
+test_tls_decrypt_lite_auth(void **ut_state)
+{
+    struct link_socket_actual from = { 0 };
+    struct tls_auth_standalone tas = { 0 };
+    struct tls_pre_decrypt_state state = { 0 };
+    enum first_packet_verdict verdict;
+
+    struct buffer buf = alloc_buf(1024);
+    tas = init_tas_auth(KEY_DIRECTION_NORMAL);
+
+    /* Packet to short to contain the hmac */
+    buf_write(&buf, client_reset_v2_none, sizeof(client_reset_v2_none));
+
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_INVALID);
+    free_tls_pre_decrypt_state(&state);
+
+    /* Valid tls-auth packet, should validate */
+    buf_reset_len(&buf);
+    buf_write(&buf, client_reset_v2_tls_auth, sizeof(client_reset_v2_tls_auth));
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_VALID_RESET);
+
+    free_tls_pre_decrypt_state(&state);
+    /* The pre decrypt function should not modify the buffer, so calling it
+     * again should have the same result */
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_VALID_RESET);
+    free_tls_pre_decrypt_state(&state);
+
+    /* and buf memory should be equal */
+    assert_memory_equal(BPTR(&buf), client_reset_v2_tls_auth, sizeof(client_reset_v2_tls_auth));
+
+    buf_reset_len(&buf);
+    buf_write(&buf, client_ack_tls_auth_randomid, sizeof(client_ack_tls_auth_randomid));
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_VALID_CONTROL_V1);
+    free_tls_pre_decrypt_state(&state);
+
+    /* flip a byte in the hmac */
+    BPTR(&buf)[20] = 0x23;
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_INVALID);
+
+    free_tls_pre_decrypt_state(&state);
+    /* Wrong key direction gives a wrong hmac key and should not validate */
+    free_key_ctx_bi(&tas.tls_wrap.opt.key_ctx_bi);
+    tas = init_tas_auth(KEY_DIRECTION_INVERSE);
+
+    buf_reset_len(&buf);
+    buf_write(&buf, client_reset_v2_tls_auth, sizeof(client_reset_v2_tls_auth));
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_INVALID);
+
+    free_tls_pre_decrypt_state(&state);
+    free_key_ctx_bi(&tas.tls_wrap.opt.key_ctx_bi);
+    free_buf(&buf);
+}
+
+void
+test_tls_decrypt_lite_none(void **ut_state)
+{
+    struct link_socket_actual from = { 0 };
+    struct tls_auth_standalone tas = { 0 };
+    struct tls_pre_decrypt_state state = { 0 };
+
+    struct buffer buf = alloc_buf(1024);
+    buf_write(&buf, client_reset_v2_tls_auth, sizeof(client_reset_v2_tls_auth));
+
+    tas.tls_wrap.mode = TLS_WRAP_NONE;
+
+    /* the method will not do additional test, so the tls-auth and tls-crypt
+     * reset will be accepted */
+    enum first_packet_verdict verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_VALID_RESET);
+    free_tls_pre_decrypt_state(&state);
+
+    buf_reset_len(&buf);
+    buf_write(&buf, client_reset_v2_none, sizeof(client_reset_v2_none));
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_VALID_RESET);
+
+    free_tls_pre_decrypt_state(&state);
+    buf_reset_len(&buf);
+    buf_write(&buf, client_reset_v2_tls_crypt, sizeof(client_reset_v2_none));
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_VALID_RESET);
+
+    free_tls_pre_decrypt_state(&state);
+
+    /* This is not a reset packet and should trigger the other response */
+    buf_reset_len(&buf);
+    buf_write(&buf, client_ack_tls_auth_randomid, sizeof(client_ack_tls_auth_randomid));
+    verdict = tls_pre_decrypt_lite(&tas, &state, &from, &buf);
+    assert_int_equal(verdict, VERDICT_VALID_CONTROL_V1);
+    free_tls_pre_decrypt_state(&state);
+    free_buf(&buf);
+}
+
+int
+main(void)
+{
+    const struct CMUnitTest tests[] = {
+        cmocka_unit_test(test_tls_decrypt_lite_none),
+        cmocka_unit_test(test_tls_decrypt_lite_auth),
+        cmocka_unit_test(test_tls_decrypt_lite_crypt),
+    };
+
+#if defined(ENABLE_CRYPTO_OPENSSL)
+    OpenSSL_add_all_algorithms();
+#endif
+
+    int ret = cmocka_run_group_tests_name("pkt tests", tests, NULL, NULL);
+
+#if defined(ENABLE_CRYPTO_OPENSSL)
+    EVP_cleanup();
+#endif
+
+    return ret;
+}