OpenVPN
test_ncp.c
Go to the documentation of this file.
1 /*
2  * OpenVPN -- An application to securely tunnel IP networks
3  * over a single UDP port, with support for SSL/TLS-based
4  * session authentication and key exchange,
5  * packet encryption, packet authentication, and
6  * packet compression.
7  *
8  * Copyright (C) 2019-2022 Arne Schwabe <arne@rfc2549.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2
12  * as published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "syshead.h"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <string.h>
36 #include <setjmp.h>
37 #include <cmocka.h>
38 
39 #include "ssl_ncp.c"
40 
41 /* Defines for use in the tests and the mock parse_line() */
42 
43 const char *bf_chacha = "BF-CBC:CHACHA20-POLY1305";
44 const char *aes_chacha = "AES-128-CBC:CHACHA20-POLY1305";
45 const char *aes_ciphers = "AES-256-GCM:AES-128-GCM";
46 
47 
48 /* Define this function here as dummy since including the ssl_*.c files
49  * leads to having to include even more unrelated code */
50 bool
52  const char *label, size_t label_size,
53  void *ekm, size_t ekm_size)
54 {
55  ASSERT(0);
56 }
57 
58 static void
60 {
61  struct gc_arena gc = gc_new();
62  bool have_chacha = cipher_valid("CHACHA20-POLY1305");
63  bool have_blowfish = cipher_valid("BF-CBC");
64 
65  assert_string_equal(mutate_ncp_cipher_list("none", &gc), "none");
66  assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:none", &gc),
67  "AES-256-GCM:none");
68 
69  assert_string_equal(mutate_ncp_cipher_list(aes_ciphers, &gc), aes_ciphers);
70 
71  if (have_chacha)
72  {
73  assert_string_equal(mutate_ncp_cipher_list(aes_chacha, &gc), aes_chacha);
74  }
75 
76  if (have_chacha && have_blowfish)
77  {
78  assert_string_equal(mutate_ncp_cipher_list(bf_chacha, &gc), bf_chacha);
79  assert_string_equal(mutate_ncp_cipher_list("BF-CBC:CHACHA20-POLY1305", &gc),
80  bf_chacha);
81  }
82  else
83  {
84  assert_ptr_equal(mutate_ncp_cipher_list(bf_chacha, &gc), NULL);
85  }
86 
87  /* Check that optional ciphers work */
88  assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:?vollbit:AES-128-GCM", &gc),
89  aes_ciphers);
90 
91  /* Check that optional ciphers work */
92  assert_string_equal(mutate_ncp_cipher_list("?AES-256-GCM:?AES-128-GCM", &gc),
93  aes_ciphers);
94 
95  /* All unsupported should still yield an empty list */
96  assert_ptr_equal(mutate_ncp_cipher_list("?kugelfisch:?grasshopper", &gc), NULL);
97 
98  /* If the last is optional, previous invalid ciphers should be ignored */
99  assert_ptr_equal(mutate_ncp_cipher_list("Vollbit:Littlebit:AES-256-CBC:BF-CBC:?nixbit", &gc), NULL);
100 
101  /* For testing that with OpenSSL 1.1.0+ that also accepts ciphers in
102  * a different spelling the normalised cipher output is the same */
103  bool have_chacha_mixed_case = cipher_valid("ChaCha20-Poly1305");
104  if (have_chacha_mixed_case)
105  {
106  assert_string_equal(mutate_ncp_cipher_list("AES-128-CBC:ChaCha20-Poly1305", &gc),
107  aes_chacha);
108  }
109 
110  assert_ptr_equal(mutate_ncp_cipher_list("vollbit", &gc), NULL);
111  assert_ptr_equal(mutate_ncp_cipher_list("AES-256-GCM:vollbit", &gc), NULL);
112  assert_ptr_equal(mutate_ncp_cipher_list("", &gc), NULL);
113 
114  assert_ptr_equal(mutate_ncp_cipher_list(
115  "ChaCha20-Poly1305:ChaCha20-Poly1305:ChaCha20-Poly1305:"
116  "ChaCha20-Poly1305:ChaCha20-Poly1305:ChaCha20-Poly1305:"
117  "ChaCha20-Poly1305", &gc), NULL);
118 
119 #ifdef ENABLE_CRYPTO_OPENSSL
120  assert_string_equal(mutate_ncp_cipher_list("id-aes128-GCM:id-aes256-GCM",
121  &gc), "AES-128-GCM:AES-256-GCM");
122 #else
123  if (have_blowfish)
124  {
125  assert_string_equal(mutate_ncp_cipher_list("BLOWFISH-CBC",
126  &gc), "BF-CBC");
127  }
128 #endif
129  gc_free(&gc);
130 }
131 
132 static void
134 {
135  struct gc_arena gc = gc_new();
136  const char *client_peer_info;
137  const char *peer_list;
138 
139  client_peer_info = "foo=bar\nIV_foo=y\nIV_NCP=2";
140  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
141  assert_string_equal(aes_ciphers, peer_list);
142  assert_true(tls_peer_supports_ncp(client_peer_info));
143 
144  client_peer_info = "foo=bar\nIV_foo=y\nIV_NCP=2\nIV_CIPHERS=BF-CBC";
145  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
146  assert_string_equal("BF-CBC", peer_list);
147  assert_true(tls_peer_supports_ncp(client_peer_info));
148 
149  client_peer_info = "IV_NCP=2\nIV_CIPHERS=BF-CBC:FOO-BAR\nIV_BAR=7";
150  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
151  assert_string_equal("BF-CBC:FOO-BAR", peer_list);
152  assert_true(tls_peer_supports_ncp(client_peer_info));
153 
154  client_peer_info = "IV_CIPHERS=BF-CBC:FOO-BAR\nIV_BAR=7";
155  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
156  assert_string_equal("BF-CBC:FOO-BAR", peer_list);
157  assert_true(tls_peer_supports_ncp(client_peer_info));
158 
159  client_peer_info = "IV_YOLO=NO\nIV_BAR=7";
160  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
161  assert_string_equal("", peer_list);
162  assert_false(tls_peer_supports_ncp(client_peer_info));
163 
164  peer_list = tls_peer_ncp_list(NULL, &gc);
165  assert_string_equal("", peer_list);
166  assert_false(tls_peer_supports_ncp(client_peer_info));
167 
168  gc_free(&gc);
169 }
170 
171 static void
172 test_poor_man(void **state)
173 {
174  struct gc_arena gc = gc_new();
175  char *best_cipher;
176 
177  const char *serverlist = "CHACHA20_POLY1305:AES-128-GCM";
178  const char *serverlistbfcbc = "CHACHA20_POLY1305:AES-128-GCM:BF-CBC:none";
179 
180  best_cipher = ncp_get_best_cipher(serverlist,
181  "IV_YOLO=NO\nIV_BAR=7",
182  "BF-CBC", &gc);
183 
184  assert_ptr_equal(best_cipher, NULL);
185 
186 
187  best_cipher = ncp_get_best_cipher(serverlistbfcbc,
188  "IV_YOLO=NO\nIV_BAR=7",
189  "BF-CBC", &gc);
190 
191  assert_string_equal(best_cipher, "BF-CBC");
192 
193 
194  best_cipher = ncp_get_best_cipher(serverlist,
195  "IV_NCP=1\nIV_BAR=7",
196  "AES-128-GCM", &gc);
197 
198  assert_string_equal(best_cipher, "AES-128-GCM");
199 
200  best_cipher = ncp_get_best_cipher(serverlist, NULL,
201  "AES-128-GCM", &gc);
202 
203  assert_string_equal(best_cipher, "AES-128-GCM");
204 
205  best_cipher = ncp_get_best_cipher(serverlist, NULL,
206  "none", &gc);
207  assert_ptr_equal(best_cipher, NULL);
208 
209  best_cipher = ncp_get_best_cipher(serverlistbfcbc, NULL,
210  "none", &gc);
211  assert_string_equal(best_cipher, "none");
212 
213  best_cipher = ncp_get_best_cipher(serverlist, NULL, NULL, &gc);
214  assert_ptr_equal(best_cipher, NULL);
215 
216  gc_free(&gc);
217 }
218 
219 
220 static void
221 test_ncp_best(void **state)
222 {
223  struct gc_arena gc = gc_new();
224  char *best_cipher;
225 
226  const char *serverlist = "CHACHA20_POLY1305:AES-128-GCM:AES-256-GCM";
227 
228  best_cipher = ncp_get_best_cipher(serverlist,
229  "IV_YOLO=NO\nIV_NCP=2\nIV_BAR=7",
230  "BF-CBC", &gc);
231 
232  assert_string_equal(best_cipher, "AES-128-GCM");
233 
234  /* Best cipher is in --cipher of client */
235  best_cipher = ncp_get_best_cipher(serverlist, "IV_NCP=2\nIV_BAR=7",
236  "CHACHA20_POLY1305", &gc);
237 
238  assert_string_equal(best_cipher, "CHACHA20_POLY1305");
239 
240  /* Best cipher is in --cipher of client */
241  best_cipher = ncp_get_best_cipher(serverlist, "IV_CIPHERS=AES-128-GCM",
242  "AES-256-CBC", &gc);
243 
244 
245  assert_string_equal(best_cipher, "AES-128-GCM");
246 
247  /* IV_NCP=2 should be ignored if IV_CIPHERS is sent */
248  best_cipher = ncp_get_best_cipher(serverlist,
249  "IV_FOO=7\nIV_CIPHERS=AES-256-GCM\nIV_NCP=2",
250  "AES-256-CBC", &gc);
251 
252  assert_string_equal(best_cipher, "AES-256-GCM");
253 
254 
255  gc_free(&gc);
256 }
257 
258 
259 
260 const struct CMUnitTest ncp_tests[] = {
261  cmocka_unit_test(test_check_ncp_ciphers_list),
262  cmocka_unit_test(test_extract_client_ciphers),
263  cmocka_unit_test(test_poor_man),
264  cmocka_unit_test(test_ncp_best)
265 };
266 
267 
268 int
269 main(void)
270 {
271 #if defined(ENABLE_CRYPTO_OPENSSL)
272  OpenSSL_add_all_algorithms();
273 #endif
274  return cmocka_run_group_tests(ncp_tests, NULL, NULL);
275 }
test_poor_man
static void test_poor_man(void **state)
Definition: test_ncp.c:172
cipher_valid
static bool cipher_valid(const char *ciphername)
Returns if the cipher is valid, based on the given cipher name.
Definition: crypto_backend.h:215
bf_chacha
const char * bf_chacha
Definition: test_ncp.c:43
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:998
config-msvc.h
ssl_ncp.c
mutate_ncp_cipher_list
char * mutate_ncp_cipher_list(const char *list, struct gc_arena *gc)
Check whether the ciphers in the supplied list are supported.
Definition: ssl_ncp.c:97
ASSERT
#define ASSERT(x)
Definition: error.h:201
key_state_export_keying_material
bool key_state_export_keying_material(struct tls_session *session, const char *label, size_t label_size, void *ekm, size_t ekm_size)
Keying Material Exporters [RFC 5705] allows additional keying material to be derived from existing TL...
Definition: test_ncp.c:51
tls_peer_ncp_list
const char * tls_peer_ncp_list(const char *peer_info, struct gc_arena *gc)
Returns the support cipher list from the peer according to the IV_NCP and IV_CIPHER values in peer_in...
Definition: ssl_ncp.c:221
test_check_ncp_ciphers_list
static void test_check_ncp_ciphers_list(void **state)
Definition: test_ncp.c:59
tls_peer_supports_ncp
bool tls_peer_supports_ncp(const char *peer_info)
Returns whether the client supports NCP either by announcing IV_NCP>=2 or the IV_CIPHERS list.
Definition: ssl_ncp.c:79
aes_chacha
const char * aes_chacha
Definition: test_ncp.c:44
main
int main(void)
Definition: test_ncp.c:269
tls_session
Security parameter state of a single session within a VPN tunnel.
Definition: ssl_common.h:439
syshead.h
test_ncp_best
static void test_ncp_best(void **state)
Definition: test_ncp.c:221
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
ncp_get_best_cipher
char * ncp_get_best_cipher(const char *server_list, const char *peer_info, const char *remote_cipher, struct gc_arena *gc)
Iterates through the ciphers in server_list and return the first cipher that is also supported by the...
Definition: ssl_ncp.c:242
aes_ciphers
const char * aes_ciphers
Definition: test_ncp.c:45
test_extract_client_ciphers
static void test_extract_client_ciphers(void **state)
Definition: test_ncp.c:133
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1006
ncp_tests
const struct CMUnitTest ncp_tests[]
Definition: test_ncp.c:260
config.h
session
Definition: keyingmaterialexporter.c:56