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