Browse code

Add --tls-crypt unit tests

These help verify the tls-crypt functionality - they already caught a
bug during development. We should however probably also add some
t_client tests once this feature is in.

To test --tls-crypt with as few dependencies as possible, this adds a
mock implementation of msg() (or actually x_msg()). For debugging
purposes, the mock implementation can be made to really log by calling
mock_set_debug_level(), but defaults to (almost) no logging.

Signed-off-by: Steffan Karger <steffan.karger@fox-it.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <1478636302-9678-6-git-send-email-steffan.karger@fox-it.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg12973.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Steffan Karger authored on 2016/11/09 05:18:22
Showing 5 changed files
... ...
@@ -2,8 +2,13 @@ AUTOMAKE_OPTIONS = foreign
2 2
 
3 3
 check_PROGRAMS = argv_testdriver
4 4
 
5
+if ENABLE_CRYPTO
6
+check_PROGRAMS += tls_crypt_testdriver
7
+endif
8
+
5 9
 TESTS = $(check_PROGRAMS)
6 10
 
11
+openvpn_includedir = $(top_srcdir)/include
7 12
 openvpn_srcdir = $(top_srcdir)/src/openvpn
8 13
 compat_srcdir = $(top_srcdir)/src/compat
9 14
 
... ...
@@ -11,7 +16,22 @@ argv_testdriver_CFLAGS  = @TEST_CFLAGS@ -I$(openvpn_srcdir) -I$(compat_srcdir) \
11 11
 	$(OPTIONAL_CRYPTO_CFLAGS)
12 12
 argv_testdriver_LDFLAGS = @TEST_LDFLAGS@ -L$(openvpn_srcdir) -Wl,--wrap=parse_line \
13 13
 	$(OPTIONAL_CRYPTO_LIBS)
14
-argv_testdriver_SOURCES = test_argv.c \
14
+argv_testdriver_SOURCES = test_argv.c mock_msg.c \
15 15
 	$(openvpn_srcdir)/platform.c \
16 16
 	$(openvpn_srcdir)/buffer.c \
17 17
 	$(openvpn_srcdir)/argv.c
18
+
19
+tls_crypt_testdriver_CFLAGS  = @TEST_CFLAGS@ \
20
+	-I$(openvpn_includedir) -I$(compat_srcdir) -I$(openvpn_srcdir) \
21
+	$(OPTIONAL_CRYPTO_CFLAGS)
22
+tls_crypt_testdriver_LDFLAGS = @TEST_LDFLAGS@ \
23
+	$(OPTIONAL_CRYPTO_LIBS)
24
+tls_crypt_testdriver_SOURCES = test_tls_crypt.c mock_msg.c \
25
+	$(openvpn_srcdir)/buffer.c \
26
+	$(openvpn_srcdir)/crypto.c \
27
+	$(openvpn_srcdir)/crypto_mbedtls.c \
28
+	$(openvpn_srcdir)/crypto_openssl.c \
29
+	$(openvpn_srcdir)/otime.c \
30
+	$(openvpn_srcdir)/packet_id.c \
31
+	$(openvpn_srcdir)/platform.c \
32
+	$(openvpn_srcdir)/tls_crypt.c
18 33
new file mode 100644
... ...
@@ -0,0 +1,92 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2016 Fox Crypto B.V. <openvpn@fox-it.com>
8
+ *
9
+ *  This program is free software; you can redistribute it and/or modify
10
+ *  it under the terms of the GNU General Public License version 2
11
+ *  as published by the Free Software Foundation.
12
+ *
13
+ *  This program is distributed in the hope that it will be useful,
14
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ *  GNU General Public License for more details.
17
+ *
18
+ *  You should have received a copy of the GNU General Public License
19
+ *  along with this program (see the file COPYING included with this
20
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
21
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ */
23
+
24
+#ifdef HAVE_CONFIG_H
25
+#include "config.h"
26
+#elif defined(_MSC_VER)
27
+#include "config-msvc.h"
28
+#endif
29
+
30
+#include <stdarg.h>
31
+#include <stdbool.h>
32
+#include <stdio.h>
33
+#include <stdlib.h>
34
+
35
+#include "errlevel.h"
36
+#include "error.h"
37
+
38
+unsigned int x_debug_level = 0; /* Default to (almost) no debugging output */
39
+bool fatal_error_triggered = false;
40
+
41
+void mock_set_debug_level(int level)
42
+{
43
+  x_debug_level = level;
44
+}
45
+
46
+void x_msg_va (const unsigned int flags, const char *format,
47
+    va_list arglist)
48
+{
49
+  if (flags & M_FATAL)
50
+    {
51
+      fatal_error_triggered = true;
52
+      printf("FATAL ERROR:");
53
+    }
54
+  vprintf(format, arglist);
55
+  printf("\n");
56
+}
57
+
58
+void x_msg (const unsigned int flags, const char *format, ...)
59
+{
60
+  va_list arglist;
61
+  va_start (arglist, format);
62
+  x_msg_va (flags, format, arglist);
63
+  va_end (arglist);
64
+}
65
+
66
+void
67
+assert_failed (const char *filename, int line, const char *condition)
68
+{
69
+  if (condition)
70
+    printf ("Assertion failed at %s:%d (%s)", filename, line, condition);
71
+  else
72
+    printf ("Assertion failed at %s:%d", filename, line);
73
+  exit (1);
74
+}
75
+
76
+/*
77
+ * Fail memory allocation.  Don't use msg() because it tries
78
+ * to allocate memory as part of its operation.
79
+ */
80
+void
81
+out_of_memory (void)
82
+{
83
+  fprintf (stderr, "Out of Memory\n");
84
+  exit (1);
85
+}
86
+
87
+bool
88
+dont_mute (unsigned int flags)
89
+{
90
+  return true;
91
+}
0 92
new file mode 100644
... ...
@@ -0,0 +1,35 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2016 Fox Crypto B.V. <openvpn@fox-it.com>
8
+ *
9
+ *  This program is free software; you can redistribute it and/or modify
10
+ *  it under the terms of the GNU General Public License version 2
11
+ *  as published by the Free Software Foundation.
12
+ *
13
+ *  This program is distributed in the hope that it will be useful,
14
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ *  GNU General Public License for more details.
17
+ *
18
+ *  You should have received a copy of the GNU General Public License
19
+ *  along with this program (see the file COPYING included with this
20
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
21
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ */
23
+
24
+#ifndef MOCK_MSG_H
25
+#define MOCK_MSG_H
26
+
27
+/**
28
+ * Mock debug level defaults to 0, which gives clean(-ish) test reports.  Call
29
+ * this function from your test driver to increase debug output when you
30
+ * need debug output.
31
+ */
32
+void mock_set_debug_level(int level);
33
+
34
+#endif /* MOCK_MSG */
... ...
@@ -14,16 +14,6 @@
14 14
 #include "buffer.h"
15 15
 
16 16
 /*
17
- * Dummy symbols that need to be defined due to them being
18
- * referenced in #include'd header files and their includes
19
- */
20
-unsigned int x_debug_level;
21
-bool dont_mute (unsigned int flags) { return true; }
22
-void assert_failed (const char *filename, int line, const char *condition) { exit(0); }
23
-void out_of_memory (void) { }
24
-void x_msg (const unsigned int flags, const char *format, ...) { }
25
-
26
-/*
27 17
  * This is defined here to prevent #include'ing misc.h
28 18
  * which makes things difficult beyond any recognition
29 19
  */
30 20
new file mode 100644
... ...
@@ -0,0 +1,242 @@
0
+/*
1
+ *  OpenVPN -- An application to securely tunnel IP networks
2
+ *             over a single UDP port, with support for SSL/TLS-based
3
+ *             session authentication and key exchange,
4
+ *             packet encryption, packet authentication, and
5
+ *             packet compression.
6
+ *
7
+ *  Copyright (C) 2016 Fox Crypto B.V. <openvpn@fox-it.com>
8
+ *
9
+ *  This program is free software; you can redistribute it and/or modify
10
+ *  it under the terms of the GNU General Public License version 2
11
+ *  as published by the Free Software Foundation.
12
+ *
13
+ *  This program is distributed in the hope that it will be useful,
14
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ *  GNU General Public License for more details.
17
+ *
18
+ *  You should have received a copy of the GNU General Public License
19
+ *  along with this program (see the file COPYING included with this
20
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
21
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
+ */
23
+
24
+#ifdef HAVE_CONFIG_H
25
+#include "config.h"
26
+#elif defined(_MSC_VER)
27
+#include "config-msvc.h"
28
+#endif
29
+
30
+#ifdef ENABLE_CRYPTO
31
+
32
+#include "syshead.h"
33
+
34
+#include <stdio.h>
35
+#include <unistd.h>
36
+#include <stdlib.h>
37
+#include <stdarg.h>
38
+#include <string.h>
39
+#include <setjmp.h>
40
+#include <cmocka.h>
41
+
42
+#include "tls_crypt.h"
43
+
44
+#include "mock_msg.h"
45
+
46
+#define TESTBUF_SIZE 		128
47
+
48
+const char plaintext_short[1];
49
+
50
+struct test_context {
51
+  struct crypto_options co;
52
+  struct key_type kt;
53
+  struct buffer source;
54
+  struct buffer ciphertext;
55
+  struct buffer unwrapped;
56
+};
57
+
58
+static int setup(void **state) {
59
+    struct test_context *ctx  = calloc(1, sizeof(*ctx));
60
+
61
+    ctx->kt.cipher = cipher_kt_get ("AES-256-CTR");
62
+    ctx->kt.cipher_length = cipher_kt_key_size (ctx->kt.cipher);
63
+    ctx->kt.digest = md_kt_get ("SHA256");
64
+    ctx->kt.hmac_length = md_kt_size (ctx->kt.digest);
65
+
66
+    struct key key = { 0 };
67
+
68
+    init_key_ctx (&ctx->co.key_ctx_bi.encrypt, &key, &ctx->kt, true, "TEST");
69
+    init_key_ctx (&ctx->co.key_ctx_bi.decrypt, &key, &ctx->kt, false, "TEST");
70
+
71
+    packet_id_init (&ctx->co.packet_id, 0, 0, "test", 0);
72
+
73
+    ctx->source = alloc_buf(TESTBUF_SIZE);
74
+    ctx->ciphertext = alloc_buf(TESTBUF_SIZE);
75
+    ctx->unwrapped = alloc_buf(TESTBUF_SIZE);
76
+
77
+    /* Write test plaintext */
78
+    buf_write(&ctx->source, plaintext_short, sizeof(plaintext_short));
79
+
80
+    /* Write dummy opcode and session id */
81
+    buf_write(&ctx->ciphertext, "012345678", 1 + 8);
82
+
83
+    *state = ctx;
84
+
85
+    return 0;
86
+}
87
+
88
+static int teardown(void **state) {
89
+    struct test_context *ctx = (struct test_context *) *state;
90
+
91
+    free_buf (&ctx->source);
92
+    free_buf (&ctx->ciphertext);
93
+    free_buf (&ctx->unwrapped);
94
+
95
+    free_key_ctx_bi (&ctx->co.key_ctx_bi);
96
+
97
+    free(ctx);
98
+
99
+    return 0;
100
+}
101
+
102
+/**
103
+ * Check that short messages are successfully wrapped-and-unwrapped.
104
+ */
105
+static void tls_crypt_loopback(void **state) {
106
+  struct test_context *ctx = (struct test_context *) *state;
107
+
108
+  assert_true (tls_crypt_wrap (&ctx->source, &ctx->ciphertext, &ctx->co));
109
+  assert_true (BLEN(&ctx->source) < BLEN(&ctx->ciphertext));
110
+  assert_true (tls_crypt_unwrap (&ctx->ciphertext, &ctx->unwrapped, &ctx->co));
111
+  assert_int_equal(BLEN(&ctx->source), BLEN(&ctx->unwrapped));
112
+  assert_memory_equal(BPTR(&ctx->source), BPTR(&ctx->unwrapped),
113
+      BLEN(&ctx->source));
114
+}
115
+
116
+/**
117
+ * Check that zero-byte messages are successfully wrapped-and-unwrapped.
118
+ */
119
+static void tls_crypt_loopback_zero_len(void **state) {
120
+  struct test_context *ctx = (struct test_context *) *state;
121
+
122
+  buf_clear(&ctx->source);
123
+
124
+  assert_true (tls_crypt_wrap (&ctx->source, &ctx->ciphertext, &ctx->co));
125
+  assert_true (BLEN(&ctx->source) < BLEN(&ctx->ciphertext));
126
+  assert_true (tls_crypt_unwrap (&ctx->ciphertext, &ctx->unwrapped, &ctx->co));
127
+  assert_int_equal(BLEN(&ctx->source), BLEN(&ctx->unwrapped));
128
+  assert_memory_equal(BPTR(&ctx->source), BPTR(&ctx->unwrapped),
129
+      BLEN(&ctx->source));
130
+}
131
+
132
+/**
133
+ * Check that max-length messages are successfully wrapped-and-unwrapped.
134
+ */
135
+static void tls_crypt_loopback_max_len(void **state) {
136
+  struct test_context *ctx = (struct test_context *) *state;
137
+
138
+  buf_clear(&ctx->source);
139
+  assert_non_null (buf_write_alloc (&ctx->source,
140
+      TESTBUF_SIZE - BLEN (&ctx->ciphertext) - tls_crypt_buf_overhead()));
141
+
142
+  assert_true (tls_crypt_wrap (&ctx->source, &ctx->ciphertext, &ctx->co));
143
+  assert_true (BLEN(&ctx->source) < BLEN(&ctx->ciphertext));
144
+  assert_true (tls_crypt_unwrap (&ctx->ciphertext, &ctx->unwrapped, &ctx->co));
145
+  assert_int_equal(BLEN(&ctx->source), BLEN(&ctx->unwrapped));
146
+  assert_memory_equal(BPTR(&ctx->source), BPTR(&ctx->unwrapped),
147
+      BLEN(&ctx->source));
148
+}
149
+
150
+/**
151
+ * Check that too-long messages are gracefully rejected.
152
+ */
153
+static void tls_crypt_fail_msg_too_long(void **state) {
154
+  struct test_context *ctx = (struct test_context *) *state;
155
+
156
+  buf_clear(&ctx->source);
157
+  assert_non_null (buf_write_alloc (&ctx->source,
158
+      TESTBUF_SIZE - BLEN (&ctx->ciphertext) - tls_crypt_buf_overhead() + 1));
159
+  assert_false (tls_crypt_wrap (&ctx->source, &ctx->ciphertext, &ctx->co));
160
+}
161
+
162
+/**
163
+ * Check that packets that were wrapped (or unwrapped) with a different key
164
+ * are not accepted.
165
+ */
166
+static void tls_crypt_fail_invalid_key(void **state) {
167
+  struct test_context *ctx = (struct test_context *) *state;
168
+
169
+  /* Change decrypt key */
170
+  struct key key = { { 1 } };
171
+  free_key_ctx (&ctx->co.key_ctx_bi.decrypt);
172
+  init_key_ctx (&ctx->co.key_ctx_bi.decrypt, &key, &ctx->kt, false, "TEST");
173
+
174
+  assert_true (tls_crypt_wrap (&ctx->source, &ctx->ciphertext, &ctx->co));
175
+  assert_true (BLEN(&ctx->source) < BLEN(&ctx->ciphertext));
176
+  assert_false (tls_crypt_unwrap (&ctx->ciphertext, &ctx->unwrapped, &ctx->co));
177
+}
178
+
179
+/**
180
+ * Check that replayed packets are not accepted.
181
+ */
182
+static void tls_crypt_fail_replay(void **state) {
183
+  struct test_context *ctx = (struct test_context *) *state;
184
+
185
+  assert_true (tls_crypt_wrap (&ctx->source, &ctx->ciphertext, &ctx->co));
186
+  assert_true (BLEN(&ctx->source) < BLEN(&ctx->ciphertext));
187
+  struct buffer tmp = ctx->ciphertext;
188
+  assert_true (tls_crypt_unwrap (&tmp, &ctx->unwrapped, &ctx->co));
189
+  buf_clear (&ctx->unwrapped);
190
+  assert_false (tls_crypt_unwrap (&ctx->ciphertext, &ctx->unwrapped, &ctx->co));
191
+}
192
+
193
+/**
194
+ * Check that packet replays are accepted when CO_IGNORE_PACKET_ID is set. This
195
+ * is used for the first control channel packet that arrives, because we don't
196
+ * know the packet ID yet.
197
+ */
198
+static void tls_crypt_ignore_replay(void **state) {
199
+  struct test_context *ctx = (struct test_context *) *state;
200
+
201
+  ctx->co.flags |= CO_IGNORE_PACKET_ID;
202
+
203
+  assert_true (tls_crypt_wrap (&ctx->source, &ctx->ciphertext, &ctx->co));
204
+  assert_true (BLEN(&ctx->source) < BLEN(&ctx->ciphertext));
205
+  struct buffer tmp = ctx->ciphertext;
206
+  assert_true (tls_crypt_unwrap (&tmp, &ctx->unwrapped, &ctx->co));
207
+  buf_clear (&ctx->unwrapped);
208
+  assert_true (tls_crypt_unwrap (&ctx->ciphertext, &ctx->unwrapped, &ctx->co));
209
+}
210
+
211
+int main(void) {
212
+    const struct CMUnitTest tests[] = {
213
+	cmocka_unit_test_setup_teardown(tls_crypt_loopback, setup, teardown),
214
+	cmocka_unit_test_setup_teardown(tls_crypt_loopback_zero_len,
215
+					setup, teardown),
216
+	cmocka_unit_test_setup_teardown(tls_crypt_loopback_max_len,
217
+					setup, teardown),
218
+	cmocka_unit_test_setup_teardown(tls_crypt_fail_msg_too_long,
219
+					setup, teardown),
220
+	cmocka_unit_test_setup_teardown(tls_crypt_fail_invalid_key,
221
+					setup, teardown),
222
+	cmocka_unit_test_setup_teardown(tls_crypt_fail_replay,
223
+					setup, teardown),
224
+	cmocka_unit_test_setup_teardown(tls_crypt_ignore_replay,
225
+					setup, teardown),
226
+    };
227
+
228
+#if defined(ENABLE_CRYPTO_OPENSSL)
229
+    OpenSSL_add_all_algorithms();
230
+#endif
231
+
232
+    int ret = cmocka_run_group_tests_name("tls-crypt tests", tests, NULL, NULL);
233
+
234
+#if defined(ENABLE_CRYPTO_OPENSSL)
235
+    EVP_cleanup();
236
+#endif
237
+
238
+    return ret;
239
+}
240
+
241
+#endif /* ENABLE_CRYPTO */