OpenVPN
xkey_helper.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/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-2025 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 "error.h"
30#include "buffer.h"
31#include "xkey_common.h"
32#include "manage.h"
33#include "base64.h"
34
35#ifdef HAVE_XKEY_PROVIDER
36
37#include <openssl/provider.h>
38#include <openssl/params.h>
39#include <openssl/core_dispatch.h>
40#include <openssl/core_object.h>
41#include <openssl/core_names.h>
42#include <openssl/store.h>
43#include <openssl/evp.h>
44#include <openssl/err.h>
45
46static const char *const props = XKEY_PROV_PROPS;
47
48XKEY_EXTERNAL_SIGN_fn xkey_management_sign;
49
50static void
51print_openssl_errors(void)
52{
53 unsigned long e;
54 while ((e = ERR_get_error()))
55 {
56 msg(M_WARN, "OpenSSL error %lu: %s", e, ERR_error_string(e, NULL));
57 }
58}
59
61int
62xkey_digest(const unsigned char *src, size_t srclen, unsigned char *buf, size_t *buflen,
63 const char *mdname)
64{
65 dmsg(D_XKEY, "In xkey_digest");
66 EVP_MD *md = EVP_MD_fetch(NULL, mdname, NULL); /* from default context */
67 if (!md)
68 {
69 msg(M_WARN, "WARN: xkey_digest: MD_fetch failed for <%s>", mdname);
70 return 0;
71 }
72
73 unsigned int len = (unsigned int)*buflen;
74 if (EVP_Digest(src, srclen, buf, &len, md, NULL) != 1)
75 {
76 msg(M_WARN, "WARN: xkey_digest: EVP_Digest failed");
77 return 0;
78 }
79 EVP_MD_free(md);
80
81 *buflen = len;
82 return 1;
83}
84
85#ifdef ENABLE_MANAGEMENT
93EVP_PKEY *
94xkey_load_management_key(OSSL_LIB_CTX *libctx, EVP_PKEY *pubkey)
95{
96 ASSERT(pubkey);
97
98 /* Management interface doesn't require any handle to be
99 * stored in the key. We use a dummy pointer as we do need a
100 * non-NULL value to indicate private key is available.
101 */
102 void *dummy = &"dummy";
103
104 XKEY_EXTERNAL_SIGN_fn *sign_op = xkey_management_sign;
105
106 return xkey_load_generic_key(libctx, dummy, pubkey, sign_op, NULL);
107}
108#endif
109
115EVP_PKEY *
116xkey_load_generic_key(OSSL_LIB_CTX *libctx, void *handle, EVP_PKEY *pubkey,
117 XKEY_EXTERNAL_SIGN_fn *sign_op, XKEY_PRIVKEY_FREE_fn *free_op)
118{
119 EVP_PKEY *pkey = NULL;
120 const char *origin = "external";
121
122 /* UTF8 string pointers in here are only read from, so cast is safe */
123 OSSL_PARAM params[] = {
124 { "xkey-origin", OSSL_PARAM_UTF8_STRING, (char *)origin, 0, 0 },
125 { "pubkey", OSSL_PARAM_OCTET_STRING, &pubkey, sizeof(pubkey), 0 },
126 { "handle", OSSL_PARAM_OCTET_PTR, &handle, sizeof(handle), 0 },
127 { "sign_op", OSSL_PARAM_OCTET_PTR, (void **)&sign_op, sizeof(sign_op), 0 },
128 { "free_op", OSSL_PARAM_OCTET_PTR, (void **)&free_op, sizeof(free_op), 0 },
129 { NULL, 0, NULL, 0, 0 }
130 };
131
132 /* Do not use EVP_PKEY_new_from_pkey as that will take keymgmt from pubkey */
133 EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(libctx, EVP_PKEY_get0_type_name(pubkey), props);
134 if (!ctx || EVP_PKEY_fromdata_init(ctx) != 1
135 || EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) != 1)
136 {
137 print_openssl_errors();
138 msg(M_FATAL, "OpenSSL error: failed to load key into ovpn.xkey provider");
139 }
140 if (ctx)
141 {
142 EVP_PKEY_CTX_free(ctx);
143 }
144
145 return pkey;
146}
147
148#ifdef ENABLE_MANAGEMENT
162int
163xkey_management_sign(void *unused, unsigned char *sig, size_t *siglen, const unsigned char *tbs,
164 size_t tbslen, XKEY_SIGALG alg)
165{
166 dmsg(D_XKEY, "In xkey_management_sign with keytype = %s, op = %s", alg.keytype, alg.op);
167
168 (void)unused;
169 char alg_str[128];
170 unsigned char buf[EVP_MAX_MD_SIZE]; /* for computing digest if required */
171 size_t buflen = sizeof(buf);
172
173 unsigned char enc[EVP_MAX_MD_SIZE + 32]; /* 32 bytes enough for digest info structure */
174 size_t enc_len = sizeof(enc);
175
176 unsigned int flags = management->settings.flags;
177 bool is_message = !strcmp(alg.op, "DigestSign"); /* tbs is message, not digest */
178
179 /* if management client cannot do digest -- we do it here */
180 if (!strcmp(alg.op, "DigestSign") && !(flags & MF_EXTERNAL_KEY_DIGEST)
181 && strcmp(alg.mdname, "none"))
182 {
183 dmsg(D_XKEY, "xkey_management_sign: computing digest");
184 if (xkey_digest(tbs, tbslen, buf, &buflen, alg.mdname))
185 {
186 tbs = buf;
187 tbslen = buflen;
188 alg.op = "Sign";
189 is_message = false;
190 }
191 else
192 {
193 return 0;
194 }
195 }
196
197 if (!strcmp(alg.keytype, "EC"))
198 {
199 if (!strcmp(alg.op, "Sign"))
200 {
201 strncpynt(alg_str, "ECDSA", sizeof(alg_str));
202 }
203 else
204 {
205 snprintf(alg_str, sizeof(alg_str), "ECDSA,hashalg=%s", alg.mdname);
206 }
207 }
208 else if (!strcmp(alg.keytype, "ED448") || !strcmp(alg.keytype, "ED25519"))
209 {
210 strncpynt(alg_str, alg.keytype, sizeof(alg_str));
211 }
212 /* else assume RSA key */
213 else if (!strcmp(alg.padmode, "pkcs1") && (flags & MF_EXTERNAL_KEY_PKCS1PAD))
214 {
215 /* For Sign, management interface expects a pkcs1 encoded digest -- add it */
216 if (!strcmp(alg.op, "Sign"))
217 {
218 if (!encode_pkcs1(enc, &enc_len, alg.mdname, tbs, tbslen))
219 {
220 return 0;
221 }
222 tbs = enc;
223 tbslen = enc_len;
224 strncpynt(alg_str, "RSA_PKCS1_PADDING", sizeof(alg_str));
225 }
226 /* For undigested message, add hashalg=digest parameter */
227 else
228 {
229 snprintf(alg_str, sizeof(alg_str), "%s,hashalg=%s", "RSA_PKCS1_PADDING", alg.mdname);
230 }
231 }
232 else if (!strcmp(alg.padmode, "none") && (flags & MF_EXTERNAL_KEY_NOPADDING)
233 && !strcmp(alg.op, "Sign")) /* NO_PADDING requires digested data */
234 {
235 strncpynt(alg_str, "RSA_NO_PADDING", sizeof(alg_str));
236 }
237 else if (!strcmp(alg.padmode, "pss") && (flags & MF_EXTERNAL_KEY_PSSPAD))
238 {
239 snprintf(alg_str, sizeof(alg_str), "%s,hashalg=%s,saltlen=%s", "RSA_PKCS1_PSS_PADDING",
240 alg.mdname, alg.saltlen);
241 }
242 else
243 {
244 msg(M_NONFATAL, "RSA padding mode not supported by management-client <%s>", alg.padmode);
245 return 0;
246 }
247
248 if (is_message)
249 {
250 strncat(alg_str, ",data=message", sizeof(alg_str) - strlen(alg_str) - 1);
251 }
252
253 dmsg(D_LOW, "xkey management_sign: requesting sig with algorithm <%s>", alg_str);
254
255 char *in_b64 = NULL;
256 char *out_b64 = NULL;
257 int len = -1;
258
259 int bencret = openvpn_base64_encode(tbs, (int)tbslen, &in_b64);
260
261 if (management && bencret > 0)
262 {
263 out_b64 = management_query_pk_sig(management, in_b64, alg_str);
264 }
265 if (out_b64)
266 {
267 len = openvpn_base64_decode(out_b64, sig, (int)*siglen);
268 }
269 free(in_b64);
270 free(out_b64);
271
272 *siglen = (len > 0) ? len : 0;
273
274 return (*siglen > 0);
275}
276#endif /* ENABLE MANAGEMENT */
277
293bool
294encode_pkcs1(unsigned char *enc, size_t *enc_len, const char *mdname, const unsigned char *tbs,
295 size_t tbslen)
296{
297 ASSERT(enc_len != NULL);
298 ASSERT(tbs != NULL);
299
300 /* Tabulate the digest info header for expected hash algorithms
301 * These were pre-computed using the DigestInfo definition:
302 * DigestInfo ::= SEQUENCE {
303 * digestAlgorithm DigestAlgorithmIdentifier,
304 * digest Digest }
305 * Also see the table in RFC 8017 section 9.2, Note 1.
306 */
307
308 const unsigned char sha1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
309 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
310 const unsigned char sha256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
311 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
312 const unsigned char sha384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
313 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
314 const unsigned char sha512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
315 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
316 const unsigned char sha224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
317 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c };
318 const unsigned char sha512_224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
319 0x65, 0x03, 0x04, 0x02, 0x05, 0x05, 0x00, 0x04, 0x1c };
320 const unsigned char sha512_256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
321 0x65, 0x03, 0x04, 0x02, 0x06, 0x05, 0x00, 0x04, 0x20 };
322
323 typedef struct
324 {
325 const int nid;
326 const unsigned char *header;
327 size_t sz;
328 } DIG_INFO;
329
330#define MAKE_DI(x) { NID_##x, x, sizeof(x) }
331
332 /* clang-format off */
333 DIG_INFO dinfo[] = {
334 MAKE_DI(sha1), MAKE_DI(sha256), MAKE_DI(sha384), MAKE_DI(sha512),
335 MAKE_DI(sha224), MAKE_DI(sha512_224), MAKE_DI(sha512_256), { 0, NULL, 0 }
336 };
337 /* clang-format on */
338
339 size_t out_len = 0;
340 bool ret = false;
341
342 int nid = OBJ_sn2nid(mdname);
343 if (nid == NID_undef)
344 {
345 /* try harder -- name variants like SHA2-256 doesn't work */
346 nid = EVP_MD_type(EVP_get_digestbyname(mdname));
347 if (nid == NID_undef)
348 {
349 msg(M_WARN, "Error: encode_pkcs11: invalid digest name <%s>", mdname);
350 goto done;
351 }
352 }
353
354 if (tbslen != EVP_MD_size(EVP_get_digestbyname(mdname)))
355 {
356 msg(M_WARN, "Error: encode_pkcs11: invalid input length <%zu>", tbslen);
357 goto done;
358 }
359
360 if (nid == NID_md5_sha1) /* no encoding needed -- just copy */
361 {
362 if (enc && (*enc_len >= tbslen))
363 {
364 memcpy(enc, tbs, tbslen);
365 ret = true;
366 }
367 out_len = tbslen;
368 goto done;
369 }
370
371 /* locate entry for nid in dinfo table */
372 DIG_INFO *di = dinfo;
373 while ((di->nid != nid) && (di->nid != 0))
374 {
375 di++;
376 }
377 if (di->nid != nid) /* not found in our table */
378 {
379 msg(M_WARN, "Error: encode_pkcs11: unsupported hash algorithm <%s>", mdname);
380 goto done;
381 }
382
383 out_len = tbslen + di->sz;
384
385 if (enc && (out_len <= *enc_len))
386 {
387 /* combine header and digest */
388 memcpy(enc, di->header, di->sz);
389 memcpy(enc + di->sz, tbs, tbslen);
390 dmsg(D_XKEY, "encode_pkcs1: digest length = %zu encoded length = %zu", tbslen, out_len);
391 ret = true;
392 }
393
394done:
395 *enc_len = out_len; /* assignment safe as out_len is > 0 at this point */
396
397 return ret;
398}
399
406int
407ecdsa_bin2der(unsigned char *buf, int len, size_t capacity)
408{
409 ECDSA_SIG *ecsig = NULL;
410 int rlen = len / 2;
411 BIGNUM *r = BN_bin2bn(buf, rlen, NULL);
412 BIGNUM *s = BN_bin2bn(buf + rlen, rlen, NULL);
413 if (!r || !s)
414 {
415 goto err;
416 }
417 ecsig = ECDSA_SIG_new(); /* this does not allocate r, s */
418 if (!ecsig)
419 {
420 goto err;
421 }
422 if (!ECDSA_SIG_set0(ecsig, r, s)) /* ecsig takes ownership of r and s */
423 {
424 ECDSA_SIG_free(ecsig);
425 goto err;
426 }
427
428 int derlen = i2d_ECDSA_SIG(ecsig, NULL);
429 if (derlen > (int)capacity)
430 {
431 ECDSA_SIG_free(ecsig);
432 msg(M_NONFATAL, "Error: DER encoded ECDSA signature is too long (%d)", derlen);
433 return 0;
434 }
435 derlen = i2d_ECDSA_SIG(ecsig, &buf);
436 ECDSA_SIG_free(ecsig);
437 return derlen;
438
439err:
440 BN_free(r); /* it is ok to free NULL BN */
441 BN_free(s);
442 return 0;
443}
444
445#endif /* HAVE_XKEY_PROVIDER */
static void strncpynt(char *dest, const char *src, size_t maxlen)
Definition buffer.h:361
#define D_XKEY
Definition errlevel.h:116
#define D_LOW
Definition errlevel.h:96
char * management_query_pk_sig(struct management *man, const char *b64_data, const char *algorithm)
Definition manage.c:3768
#define MF_EXTERNAL_KEY_PKCS1PAD
Definition manage.h:38
#define MF_EXTERNAL_KEY_PSSPAD
Definition manage.h:43
#define MF_EXTERNAL_KEY_NOPADDING
Definition manage.h:37
#define MF_EXTERNAL_KEY_DIGEST
Definition manage.h:44
void OSSL_LIB_CTX
static void EVP_MD_free(const EVP_MD *md)
static const EVP_MD * EVP_MD_fetch(void *ctx, const char *algorithm, const char *properties)
#define M_FATAL
Definition error.h:88
#define M_NONFATAL
Definition error.h:89
#define dmsg(flags,...)
Definition error.h:170
#define msg(flags,...)
Definition error.h:150
#define ASSERT(x)
Definition error.h:217
#define M_WARN
Definition error.h:90
int openvpn_base64_decode(const char *str, void *data, int size)
Definition base64.c:157
int openvpn_base64_encode(const void *data, int size, char **str)
Definition base64.c:51
unsigned int flags
Definition manage.h:241
struct man_settings settings
Definition manage.h:334