OpenVPN
test_provider.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) 2021-2023 Selva Nair <selva.nair@gmail.com>
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 as published by the
12  * Free Software Foundation, either version 2 of the License,
13  * or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28 
29 #include "syshead.h"
30 #include "manage.h"
31 #include "integer.h"
32 #include "xkey_common.h"
33 
34 #ifdef HAVE_XKEY_PROVIDER
35 
36 #include <setjmp.h>
37 #include <cmocka.h>
38 #include <openssl/bio.h>
39 #include <openssl/pem.h>
40 #include <openssl/core_names.h>
41 #include <openssl/evp.h>
42 
43 #include "test_common.h"
44 
45 struct management *management; /* global */
46 static int mgmt_callback_called;
47 
48 #ifndef _countof
49 #define _countof(x) sizeof((x))/sizeof(*(x))
50 #endif
51 
52 static OSSL_PROVIDER *prov[2];
53 
54 /* public keys for testing -- RSA and EC */
55 static const char pubkey1[] = "-----BEGIN PUBLIC KEY-----\n"
56  "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7GWP6RLCGlvmVioIqYI6\n"
57  "LUR4owA7sJ/nJxBAk+/xzD6gqgSigBsTqeb+gdZwkKjY1N4w2DUA0r5i8Eja/BWN\n"
58  "xMZtC5nxK4MACtMqIwvlzfk130NhFXKtlZj2cyFBXqDdRyeg1ZrUQagcHVcgcReP\n"
59  "9yiePgfO7NUOQk8edEeOR53SFCgnLBQQ9dGWtZN0hO/5BN6NSm/fd6vq0VjTRP5a\n"
60  "BAH/BnqX9/3jV0jh8N9AE59mI1rjVVQ9VDnuAPkS8dLfdC661/CNxt0YWByTIgt1\n"
61  "+qjW4LUvLbnU/rlPhuJ1SBZg+z/JtDBCKfs7syu5WYFqRvNFg7/91Rr/NwxvW/1h\n"
62  "8QIDAQAB\n"
63  "-----END PUBLIC KEY-----\n";
64 
65 static const char pubkey2[] = "-----BEGIN PUBLIC KEY-----\n"
66  "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEO85iXW+HgnUkwlj1DohNVw0GsnGIh1gZ\n"
67  "u95ff1JiUaJIkYNIkZA+hwIPFVH5aJcSCv3SPIeDS2VUAESNKHZJBQ==\n"
68  "-----END PUBLIC KEY-----\n";
69 
70 static const char pubkey3[] = "-----BEGIN PUBLIC KEY-----\n"
71  "MCowBQYDK2VwAyEA+q5xjF5hGyyqYZidJdz/0saEQabL3N4wIZJBxNGbgJE=\n"
72  "-----END PUBLIC KEY-----";
73 
74 static const char *pubkeys[] = {pubkey1, pubkey2, pubkey3};
75 
76 static const char *prov_name = "ovpn.xkey";
77 
78 static const char *test_msg = "Lorem ipsum dolor sit amet, consectetur "
79  "adipisici elit, sed eiusmod tempor incidunt "
80  "ut labore et dolore magna aliqua.";
81 
82 static const char *test_msg_b64 =
83  "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2ljaS"
84  "BlbGl0LCBzZWQgZWl1c21vZCB0ZW1wb3IgaW5jaWR1bnQgdXQgbGFib3JlIGV0IGRv"
85  "bG9yZSBtYWduYSBhbGlxdWEu";
86 
87 /* Sha256 digest of test_msg excluding NUL terminator */
88 static const uint8_t test_digest[] = {
89  0x77, 0x38, 0x65, 0x00, 0x1e, 0x96, 0x48, 0xc6, 0x57, 0x0b, 0xae,
90  0xc0, 0xb7, 0x96, 0xf9, 0x66, 0x4d, 0x5f, 0xd0, 0xb7, 0xdb, 0xf3,
91  0x3a, 0xbf, 0x02, 0xcc, 0x78, 0x61, 0x83, 0x20, 0x20, 0xee
92 };
93 
94 static const char *test_digest_b64 = "dzhlAB6WSMZXC67At5b5Zk1f0Lfb8zq/Asx4YYMgIO4=";
95 
96 /* Dummy signature used only to check that the expected callback
97  * was successfully exercised. Keep this shorter than 64 bytes
98  * --- the smallest size of the actual signature with the above
99  * keys.
100  */
101 static const uint8_t good_sig[] = {
102  0xd8, 0xa7, 0xd9, 0x81, 0xd8, 0xaa, 0xd8, 0xad, 0x20, 0xd9, 0x8a, 0xd8,
103  0xa7, 0x20, 0xd8, 0xb3, 0xd9, 0x85, 0xd8, 0xb3, 0xd9, 0x85, 0x0
104 };
105 
106 static const char *good_sig_b64 = "2KfZgdiq2K0g2YrYpyDYs9mF2LPZhQA=";
107 
108 static EVP_PKEY *
109 load_pubkey(const char *pem)
110 {
111  BIO *in = BIO_new_mem_buf(pem, -1);
112  assert_non_null(in);
113 
114  EVP_PKEY *pkey = PEM_read_bio_PUBKEY(in, NULL, NULL, NULL);
115  assert_non_null(pkey);
116 
117  BIO_free(in);
118  return pkey;
119 }
120 
121 static void
122 init_test()
123 {
125  prov[0] = OSSL_PROVIDER_load(NULL, "default");
126  OSSL_PROVIDER_add_builtin(NULL, prov_name, xkey_provider_init);
127  prov[1] = OSSL_PROVIDER_load(NULL, prov_name);
128 
129  /* set default propq matching what we use in ssl_openssl.c */
130  EVP_set_default_properties(NULL, "?provider!=ovpn.xkey");
131 
132 #ifdef ENABLE_MANAGEMENT
133  management = test_calloc(sizeof(*management), 1);
134 #endif
135 }
136 
137 static void
138 uninit_test()
139 {
140  for (size_t i = 0; i < _countof(prov); i++)
141  {
142  if (prov[i])
143  {
144  OSSL_PROVIDER_unload(prov[i]);
145  }
146  }
147  test_free(management);
148 }
149 
150 /* Mock management callback for signature.
151  * We check that the received data to sign matches test_msg or
152  * test_digest and return a predefined string as signature so that
153  * the caller can validate all steps up to sending the data to
154  * the management client.
155  */
156 char *
157 management_query_pk_sig(struct management *man, const char *b64_data,
158  const char *algorithm)
159 {
160  char *out = NULL;
161 
162  /* indicate entry to the callback */
163  mgmt_callback_called = 1;
164 
165  const char *expected_tbs = test_digest_b64;
166  if (strstr(algorithm, "data=message"))
167  {
168  expected_tbs = test_msg_b64;
169  /* ED25519 does not have a hash algorithm even though it goes via
170  * the DigestSign path (data=message) */
171  if (!strstr(algorithm, "ED25519"))
172  {
173  assert_non_null(strstr(algorithm, "hashalg=SHA256"));
174  }
175  }
176  assert_string_equal(b64_data, expected_tbs);
177 
178  /* We test using ED25519, ECDSA or PSS with saltlen = digest */
179  if (!strstr(algorithm, "ECDSA") && !strstr(algorithm, "ED25519"))
180  {
181  assert_non_null(strstr(algorithm, "RSA_PKCS1_PSS_PADDING,hashalg=SHA256,saltlen=digest"));
182  }
183 
184  /* Return a predefined string as sig so that the caller
185  * can confirm that this callback was exercised.
186  */
187  out = strdup(good_sig_b64);
188  assert_non_null(out);
189 
190  return out;
191 }
192 
193 /* Check signature and keymgmt methods can be fetched from the provider */
194 static void
195 xkey_provider_test_fetch(void **state)
196 {
197  assert_true(OSSL_PROVIDER_available(NULL, prov_name));
198 
199  const char *algs[] = {"RSA", "ECDSA"};
200 
201  for (size_t i = 0; i < _countof(algs); i++)
202  {
203  EVP_SIGNATURE *sig = EVP_SIGNATURE_fetch(NULL, algs[i], "provider=ovpn.xkey");
204  assert_non_null(sig);
205  assert_string_equal(OSSL_PROVIDER_get0_name(EVP_SIGNATURE_get0_provider(sig)), prov_name);
206 
207  EVP_SIGNATURE_free(sig);
208  }
209 
210  const char *names[] = {"RSA", "EC"};
211 
212  for (size_t i = 0; i < _countof(names); i++)
213  {
214  EVP_KEYMGMT *km = EVP_KEYMGMT_fetch(NULL, names[i], "provider=ovpn.xkey");
215  assert_non_null(km);
216  assert_string_equal(OSSL_PROVIDER_get0_name(EVP_KEYMGMT_get0_provider(km)), prov_name);
217 
218  EVP_KEYMGMT_free(km);
219  }
220 }
221 
222 /* sign a test message using pkey -- caller must free the returned sig */
223 static uint8_t *
224 digest_sign(EVP_PKEY *pkey)
225 {
226  uint8_t *sig = NULL;
227  size_t siglen = 0;
228 
229  OSSL_PARAM params[6] = {OSSL_PARAM_END};
230 
231  const char *mdname = "SHA256";
232  const char *padmode = "pss";
233  const char *saltlen = "digest";
234 
235  if (EVP_PKEY_get_id(pkey) == EVP_PKEY_RSA)
236  {
237  params[0] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, (char *)mdname, 0);
238  params[1] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, (char *)padmode, 0);
239  params[2] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, (char *)saltlen, 0);
240  /* same digest for mgf1 */
241  params[3] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, (char *)saltlen, 0);
242  params[4] = OSSL_PARAM_construct_end();
243  }
244  else if (EVP_PKEY_get_id(pkey) == EVP_PKEY_ED25519)
245  {
246  mdname = NULL;
247  params[0] = OSSL_PARAM_construct_end();
248  }
249 
250 
251  EVP_PKEY_CTX *pctx = NULL;
252  EVP_MD_CTX *mctx = EVP_MD_CTX_new();
253 
254  if (!mctx
255  || EVP_DigestSignInit_ex(mctx, &pctx, mdname, NULL, NULL, pkey, params) <= 0)
256  {
257  fail_msg("Failed to initialize EVP_DigestSignInit_ex()");
258  goto done;
259  }
260 
261  /* sign with sig = NULL to get required siglen */
262  assert_int_equal(EVP_DigestSign(mctx, sig, &siglen, (uint8_t *)test_msg, strlen(test_msg)), 1);
263  assert_true(siglen > 0);
264 
265  if ((sig = test_calloc(1, siglen)) == NULL)
266  {
267  fail_msg("Out of memory");
268  }
269  assert_int_equal(EVP_DigestSign(mctx, sig, &siglen, (uint8_t *)test_msg, strlen(test_msg)), 1);
270 
271 done:
272  if (mctx)
273  {
274  EVP_MD_CTX_free(mctx); /* pctx is internally allocated and freed by mctx */
275  }
276  return sig;
277 }
278 
279 #ifdef ENABLE_MANAGEMENT
280 /* Check loading of management external key and have sign callback exercised
281  * for RSA and EC keys with and without digest support in management client.
282  * Sha256 digest used for both cases with pss padding for RSA.
283  */
284 static void
285 xkey_provider_test_mgmt_sign_cb(void **state)
286 {
287  EVP_PKEY *pubkey;
288  for (size_t i = 0; i < _countof(pubkeys); i++)
289  {
290  pubkey = load_pubkey(pubkeys[i]);
291  assert_true(pubkey != NULL);
292  EVP_PKEY *privkey = xkey_load_management_key(NULL, pubkey);
293  assert_true(privkey != NULL);
294 
296 
297  /* first without digest support in management client */
298 again:
299  mgmt_callback_called = 0;
300  uint8_t *sig = digest_sign(privkey);
301  assert_non_null(sig);
302 
303  /* check callback for signature got exercised */
304  assert_int_equal(mgmt_callback_called, 1);
305  assert_memory_equal(sig, good_sig, sizeof(good_sig));
306  test_free(sig);
307 
309  {
311  goto again; /* this time with digest support announced */
312  }
313 
314  EVP_PKEY_free(pubkey);
315  EVP_PKEY_free(privkey);
316  }
317 }
318 #endif /* ifdef ENABLE_MANAGEMENT */
319 
320 /* helpers for testing generic key load and sign */
321 static int xkey_free_called;
322 static int xkey_sign_called;
323 static void
324 xkey_free(void *handle)
325 {
326  xkey_free_called = 1;
327  /* We use a dummy string as handle -- check its value */
328  assert_string_equal(handle, "xkey_handle");
329 }
330 
331 static int
332 xkey_sign(void *handle, unsigned char *sig, size_t *siglen,
333  const unsigned char *tbs, size_t tbslen, XKEY_SIGALG s)
334 {
335  if (!sig)
336  {
337  *siglen = 256; /* some arbitrary size */
338  return 1;
339  }
340 
341  xkey_sign_called = 1; /* called with non-null sig */
342 
343  if (!strcmp(s.op, "DigestSign"))
344  {
345  assert_memory_equal(tbs, test_msg, strlen(test_msg));
346  }
347  else
348  {
349  assert_memory_equal(tbs, test_digest, sizeof(test_digest));
350  }
351 
352  /* For the test use sha256 and PSS padding for RSA and none for EDDSA */
353  if (!strcmp(s.keytype, "ED25519"))
354  {
355  assert_string_equal(s.mdname, "none");
356  }
357  else
358  {
359  assert_int_equal(OBJ_sn2nid(s.mdname), NID_sha256);
360  }
361  if (!strcmp(s.keytype, "RSA"))
362  {
363  assert_string_equal(s.padmode, "pss"); /* we use PSS for the test */
364  }
365  else if (strcmp(s.keytype, "EC") && strcmp(s.keytype, "ED25519"))
366  {
367  fail_msg("Unknown keytype: %s", s.keytype);
368  }
369 
370  /* return a predefined string as sig */
371  memcpy(sig, good_sig, min_int(sizeof(good_sig), *siglen));
372 
373  return 1;
374 }
375 
376 /* Load a key as a generic key and check its sign op gets
377  * called for signature.
378  */
379 static void
380 xkey_provider_test_generic_sign_cb(void **state)
381 {
382  EVP_PKEY *pubkey;
383  const char *dummy = "xkey_handle"; /* a dummy handle for the external key */
384 
385  for (size_t i = 0; i < _countof(pubkeys); i++)
386  {
387  pubkey = load_pubkey(pubkeys[i]);
388  assert_true(pubkey != NULL);
389 
390  EVP_PKEY *privkey = xkey_load_generic_key(NULL, (void *)dummy, pubkey, xkey_sign, xkey_free);
391  assert_true(privkey != NULL);
392 
393  xkey_sign_called = 0;
394  xkey_free_called = 0;
395  uint8_t *sig = digest_sign(privkey);
396  assert_non_null(sig);
397 
398  /* check callback for signature got exercised */
399  assert_int_equal(xkey_sign_called, 1);
400  assert_memory_equal(sig, good_sig, sizeof(good_sig));
401  test_free(sig);
402 
403  EVP_PKEY_free(pubkey);
404  EVP_PKEY_free(privkey);
405 
406  /* check key's free-op got called */
407  assert_int_equal(xkey_free_called, 1);
408  }
409 }
410 
411 int
412 main(void)
413 {
414  init_test();
415 
416  const struct CMUnitTest tests[] = {
417  cmocka_unit_test(xkey_provider_test_fetch),
418 #ifdef ENABLE_MANAGEMENT
419  cmocka_unit_test(xkey_provider_test_mgmt_sign_cb),
420 #endif
421  cmocka_unit_test(xkey_provider_test_generic_sign_cb),
422  };
423 
424  int ret = cmocka_run_group_tests_name("xkey provider tests", tests, NULL, NULL);
425 
426  uninit_test();
427  return ret;
428 }
429 #else /* ifdef HAVE_XKEY_PROVIDER */
430 int
431 main(void)
432 {
433  return 0;
434 }
435 #endif /* HAVE_XKEY_PROVIDER */
MF_EXTERNAL_KEY_PSSPAD
#define MF_EXTERNAL_KEY_PSSPAD
Definition: manage.h:44
management::settings
struct man_settings settings
Definition: manage.h:338
openvpn_unit_test_setup
static void openvpn_unit_test_setup()
Sets up the environment for unit tests like making both stderr and stdout non-buffered to avoid messa...
Definition: test_common.h:36
manage.h
test_common.h
EVP_MD_CTX_new
static EVP_MD_CTX * EVP_MD_CTX_new(void)
Allocate a new message digest object.
Definition: openssl_compat.h:114
xkey_common.h
MF_EXTERNAL_KEY_DIGEST
#define MF_EXTERNAL_KEY_DIGEST
Definition: manage.h:45
MF_EXTERNAL_KEY
#define MF_EXTERNAL_KEY
Definition: manage.h:37
EVP_MD_CTX_free
static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
Free an existing message digest context.
Definition: openssl_compat.h:103
management_query_pk_sig
char * management_query_pk_sig(struct management *man, const char *b64_data, const char *algorithm)
Definition: manage.c:3747
main
int main(void)
Definition: test_provider.c:431
syshead.h
management
Definition: manage.h:335
management
struct management * management
Definition: manage.c:63
min_int
static int min_int(int x, int y)
Definition: integer.h:89
OSSL_PROVIDER
void OSSL_PROVIDER
Definition: openssl_compat.h:775
config.h
integer.h
man_settings::flags
unsigned int flags
Definition: manage.h:246