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-2024 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 #endif
27 
28 #include "syshead.h"
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <setjmp.h>
35 #include <cmocka.h>
36 
37 #include "ssl_ncp.c"
38 #include "test_common.h"
39 
40 /* Defines for use in the tests and the mock parse_line() */
41 
42 const char *bf_chacha = "BF-CBC:CHACHA20-POLY1305";
43 const char *aes_chacha = "AES-128-CBC:CHACHA20-POLY1305";
44 const char *aes_ciphers = "AES-256-GCM:AES-128-GCM";
45 
46 
47 /* Define this function here as dummy since including the ssl_*.c files
48  * leads to having to include even more unrelated code */
49 bool
51  const char *label, size_t label_size,
52  void *ekm, size_t ekm_size)
53 {
54  ASSERT(0);
55 }
56 
57 static void
59 {
60  struct gc_arena gc = gc_new();
61  bool have_chacha = cipher_valid("CHACHA20-POLY1305");
62  bool have_blowfish = cipher_valid("BF-CBC");
63 
64  assert_string_equal(mutate_ncp_cipher_list("none", &gc), "none");
65  assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:none", &gc),
66  "AES-256-GCM:none");
67 
68  assert_string_equal(mutate_ncp_cipher_list(aes_ciphers, &gc), aes_ciphers);
69 
70  if (have_chacha)
71  {
72  assert_string_equal(mutate_ncp_cipher_list(aes_chacha, &gc), aes_chacha);
73  }
74 
75  if (have_chacha && have_blowfish)
76  {
77  assert_string_equal(mutate_ncp_cipher_list(bf_chacha, &gc), bf_chacha);
78  assert_string_equal(mutate_ncp_cipher_list("BF-CBC:CHACHA20-POLY1305", &gc),
79  bf_chacha);
80  }
81  else
82  {
83  assert_ptr_equal(mutate_ncp_cipher_list(bf_chacha, &gc), NULL);
84  }
85 
86  /* Check that optional ciphers work */
87  assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:?vollbit:AES-128-GCM", &gc),
88  aes_ciphers);
89 
90  /* Check that optional ciphers work */
91  assert_string_equal(mutate_ncp_cipher_list("?AES-256-GCM:?AES-128-GCM", &gc),
92  aes_ciphers);
93 
94  /* All unsupported should still yield an empty list */
95  assert_ptr_equal(mutate_ncp_cipher_list("?kugelfisch:?grasshopper", &gc), NULL);
96 
97  /* If the last is optional, previous invalid ciphers should be ignored */
98  assert_ptr_equal(mutate_ncp_cipher_list("Vollbit:Littlebit:AES-256-CBC:BF-CBC:?nixbit", &gc), NULL);
99 
100  /* We do not support CCM ciphers */
101  assert_ptr_equal(mutate_ncp_cipher_list("AES-256-GCM:AES-128-CCM", &gc), NULL);
102 
103  assert_string_equal(mutate_ncp_cipher_list("AES-256-GCM:?AES-128-CCM:AES-128-GCM", &gc),
104  aes_ciphers);
105 
106  /* For testing that with OpenSSL 1.1.0+ that also accepts ciphers in
107  * a different spelling the normalised cipher output is the same */
108  bool have_chacha_mixed_case = cipher_valid("ChaCha20-Poly1305");
109  if (have_chacha_mixed_case)
110  {
111  assert_string_equal(mutate_ncp_cipher_list("AES-128-CBC:ChaCha20-Poly1305", &gc),
112  aes_chacha);
113  }
114 
115  assert_ptr_equal(mutate_ncp_cipher_list("vollbit", &gc), NULL);
116  assert_ptr_equal(mutate_ncp_cipher_list("AES-256-GCM:vollbit", &gc), NULL);
117  assert_ptr_equal(mutate_ncp_cipher_list("", &gc), NULL);
118 
119  assert_ptr_equal(mutate_ncp_cipher_list(
120  "ChaCha20-Poly1305:ChaCha20-Poly1305:ChaCha20-Poly1305:"
121  "ChaCha20-Poly1305:ChaCha20-Poly1305:ChaCha20-Poly1305:"
122  "ChaCha20-Poly1305", &gc), NULL);
123 
124 #ifdef ENABLE_CRYPTO_OPENSSL
125  assert_string_equal(mutate_ncp_cipher_list("id-aes128-GCM:id-aes256-GCM",
126  &gc), "AES-128-GCM:AES-256-GCM");
127 #else
128  if (have_blowfish)
129  {
130  assert_string_equal(mutate_ncp_cipher_list("BLOWFISH-CBC",
131  &gc), "BF-CBC");
132  }
133 #endif
134  gc_free(&gc);
135 }
136 
137 static void
139 {
140  struct gc_arena gc = gc_new();
141  const char *client_peer_info;
142  const char *peer_list;
143 
144  client_peer_info = "foo=bar\nIV_foo=y\nIV_NCP=2";
145  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
146  assert_string_equal(aes_ciphers, peer_list);
147  assert_true(tls_peer_supports_ncp(client_peer_info));
148 
149  client_peer_info = "foo=bar\nIV_foo=y\nIV_NCP=2\nIV_CIPHERS=BF-CBC";
150  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
151  assert_string_equal("BF-CBC", peer_list);
152  assert_true(tls_peer_supports_ncp(client_peer_info));
153 
154  client_peer_info = "IV_NCP=2\nIV_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_CIPHERS=BF-CBC:FOO-BAR\nIV_BAR=7";
160  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
161  assert_string_equal("BF-CBC:FOO-BAR", peer_list);
162  assert_true(tls_peer_supports_ncp(client_peer_info));
163 
164  client_peer_info = "IV_YOLO=NO\nIV_BAR=7";
165  peer_list = tls_peer_ncp_list(client_peer_info, &gc);
166  assert_string_equal("", peer_list);
167  assert_false(tls_peer_supports_ncp(client_peer_info));
168 
169  peer_list = tls_peer_ncp_list(NULL, &gc);
170  assert_string_equal("", peer_list);
171  assert_false(tls_peer_supports_ncp(client_peer_info));
172 
173  gc_free(&gc);
174 }
175 
176 static void
177 test_poor_man(void **state)
178 {
179  struct gc_arena gc = gc_new();
180  char *best_cipher;
181 
182  const char *serverlist = "CHACHA20_POLY1305:AES-128-GCM";
183  const char *serverlistbfcbc = "CHACHA20_POLY1305:AES-128-GCM:BF-CBC:none";
184 
185  best_cipher = ncp_get_best_cipher(serverlist,
186  "IV_YOLO=NO\nIV_BAR=7",
187  "BF-CBC", &gc);
188 
189  assert_ptr_equal(best_cipher, NULL);
190 
191 
192  best_cipher = ncp_get_best_cipher(serverlistbfcbc,
193  "IV_YOLO=NO\nIV_BAR=7",
194  "BF-CBC", &gc);
195 
196  assert_string_equal(best_cipher, "BF-CBC");
197 
198 
199  best_cipher = ncp_get_best_cipher(serverlist,
200  "IV_NCP=1\nIV_BAR=7",
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  "AES-128-GCM", &gc);
207 
208  assert_string_equal(best_cipher, "AES-128-GCM");
209 
210  best_cipher = ncp_get_best_cipher(serverlist, NULL,
211  "none", &gc);
212  assert_ptr_equal(best_cipher, NULL);
213 
214  best_cipher = ncp_get_best_cipher(serverlistbfcbc, NULL,
215  "none", &gc);
216  assert_string_equal(best_cipher, "none");
217 
218  best_cipher = ncp_get_best_cipher(serverlist, NULL, NULL, &gc);
219  assert_ptr_equal(best_cipher, NULL);
220 
221  gc_free(&gc);
222 }
223 
224 
225 static void
226 test_ncp_best(void **state)
227 {
228  struct gc_arena gc = gc_new();
229  char *best_cipher;
230 
231  const char *serverlist = "CHACHA20_POLY1305:AES-128-GCM:AES-256-GCM";
232 
233  best_cipher = ncp_get_best_cipher(serverlist,
234  "IV_YOLO=NO\nIV_NCP=2\nIV_BAR=7",
235  "BF-CBC", &gc);
236 
237  assert_string_equal(best_cipher, "AES-128-GCM");
238 
239  /* Best cipher is in --cipher of client */
240  best_cipher = ncp_get_best_cipher(serverlist, "IV_NCP=2\nIV_BAR=7",
241  "CHACHA20_POLY1305", &gc);
242 
243  assert_string_equal(best_cipher, "CHACHA20_POLY1305");
244 
245  /* Best cipher is in --cipher of client */
246  best_cipher = ncp_get_best_cipher(serverlist, "IV_CIPHERS=AES-128-GCM",
247  "AES-256-CBC", &gc);
248 
249 
250  assert_string_equal(best_cipher, "AES-128-GCM");
251 
252  /* IV_NCP=2 should be ignored if IV_CIPHERS is sent */
253  best_cipher = ncp_get_best_cipher(serverlist,
254  "IV_FOO=7\nIV_CIPHERS=AES-256-GCM\nIV_NCP=2",
255  "AES-256-CBC", &gc);
256 
257  assert_string_equal(best_cipher, "AES-256-GCM");
258 
259 
260  gc_free(&gc);
261 }
262 
263 
264 
265 const struct CMUnitTest ncp_tests[] = {
266  cmocka_unit_test(test_check_ncp_ciphers_list),
267  cmocka_unit_test(test_extract_client_ciphers),
268  cmocka_unit_test(test_poor_man),
269  cmocka_unit_test(test_ncp_best)
270 };
271 
272 
273 int
274 main(void)
275 {
277 #if defined(ENABLE_CRYPTO_OPENSSL)
278  OpenSSL_add_all_algorithms();
279 #endif
280  return cmocka_run_group_tests(ncp_tests, NULL, NULL);
281 }
test_poor_man
static void test_poor_man(void **state)
Definition: test_ncp.c:177
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:204
bf_chacha
const char * bf_chacha
Definition: test_ncp.c:42
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1030
test_common.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:95
ASSERT
#define ASSERT(x)
Definition: error.h:195
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:50
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:227
test_check_ncp_ciphers_list
static void test_check_ncp_ciphers_list(void **state)
Definition: test_ncp.c:58
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:77
aes_chacha
const char * aes_chacha
Definition: test_ncp.c:43
main
int main(void)
Definition: test_ncp.c:274
tls_session
Security parameter state of a single session within a VPN tunnel.
Definition: ssl_common.h:471
syshead.h
test_ncp_best
static void test_ncp_best(void **state)
Definition: test_ncp.c:226
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
openvpn_unit_test_setup
static void openvpn_unit_test_setup(void)
Sets up the environment for unit tests like making both stderr and stdout non-buffered to avoid messa...
Definition: test_common.h:36
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:248
aes_ciphers
const char * aes_ciphers
Definition: test_ncp.c:44
test_extract_client_ciphers
static void test_extract_client_ciphers(void **state)
Definition: test_ncp.c:138
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1038
ncp_tests
const struct CMUnitTest ncp_tests[]
Definition: test_ncp.c:265
config.h
session
Definition: keyingmaterialexporter.c:56