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-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 "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 strlcpy(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 strlcpy(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 strlcpy(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 strlcpy(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 if (strlcat(alg_str, ",data=message", sizeof(alg_str)) >= sizeof(alg_str))
251 {
252 return 0;
253 }
254 }
255
256 dmsg(D_LOW, "xkey management_sign: requesting sig with algorithm <%s>", alg_str);
257
258 char *in_b64 = NULL;
259 char *out_b64 = NULL;
260 int len = -1;
261
262 int bencret = openvpn_base64_encode(tbs, (int)tbslen, &in_b64);
263
264 if (management && bencret > 0)
265 {
266 out_b64 = management_query_pk_sig(management, in_b64, alg_str);
267 }
268 if (out_b64)
269 {
270 len = openvpn_base64_decode(out_b64, sig, (int)*siglen);
271 }
272 free(in_b64);
273 free(out_b64);
274
275 *siglen = (len > 0) ? len : 0;
276
277 return (*siglen > 0);
278}
279#endif /* ENABLE MANAGEMENT */
280
296bool
297encode_pkcs1(unsigned char *enc, size_t *enc_len, const char *mdname, const unsigned char *tbs,
298 size_t tbslen)
299{
300 ASSERT(enc_len != NULL);
301 ASSERT(tbs != NULL);
302
303 /* Tabulate the digest info header for expected hash algorithms
304 * These were pre-computed using the DigestInfo definition:
305 * DigestInfo ::= SEQUENCE {
306 * digestAlgorithm DigestAlgorithmIdentifier,
307 * digest Digest }
308 * Also see the table in RFC 8017 section 9.2, Note 1.
309 */
310
311 const unsigned char sha1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
312 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
313 const unsigned char sha256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
314 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };
315 const unsigned char sha384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
316 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };
317 const unsigned char sha512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
318 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };
319 const unsigned char sha224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
320 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c };
321 const unsigned char sha512_224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
322 0x65, 0x03, 0x04, 0x02, 0x05, 0x05, 0x00, 0x04, 0x1c };
323 const unsigned char sha512_256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
324 0x65, 0x03, 0x04, 0x02, 0x06, 0x05, 0x00, 0x04, 0x20 };
325
326 typedef struct
327 {
328 const int nid;
329 const unsigned char *header;
330 size_t sz;
331 } DIG_INFO;
332
333#define MAKE_DI(x) { NID_##x, x, sizeof(x) }
334
335 /* clang-format off */
336 DIG_INFO dinfo[] = {
337 MAKE_DI(sha1), MAKE_DI(sha256), MAKE_DI(sha384), MAKE_DI(sha512),
338 MAKE_DI(sha224), MAKE_DI(sha512_224), MAKE_DI(sha512_256), { 0, NULL, 0 }
339 };
340 /* clang-format on */
341
342 size_t out_len = 0;
343 bool ret = false;
344
345 int nid = OBJ_sn2nid(mdname);
346 if (nid == NID_undef)
347 {
348 /* try harder -- name variants like SHA2-256 doesn't work */
349 nid = EVP_MD_type(EVP_get_digestbyname(mdname));
350 if (nid == NID_undef)
351 {
352 msg(M_WARN, "Error: encode_pkcs11: invalid digest name <%s>", mdname);
353 goto done;
354 }
355 }
356
357 if (tbslen != (size_t)EVP_MD_size(EVP_get_digestbyname(mdname)))
358 {
359 msg(M_WARN, "Error: encode_pkcs11: invalid input length <%zu>", tbslen);
360 goto done;
361 }
362
363 if (nid == NID_md5_sha1) /* no encoding needed -- just copy */
364 {
365 if (enc && (*enc_len >= tbslen))
366 {
367 memcpy(enc, tbs, tbslen);
368 ret = true;
369 }
370 out_len = tbslen;
371 goto done;
372 }
373
374 /* locate entry for nid in dinfo table */
375 DIG_INFO *di = dinfo;
376 while ((di->nid != nid) && (di->nid != 0))
377 {
378 di++;
379 }
380 if (di->nid != nid) /* not found in our table */
381 {
382 msg(M_WARN, "Error: encode_pkcs11: unsupported hash algorithm <%s>", mdname);
383 goto done;
384 }
385
386 out_len = tbslen + di->sz;
387
388 if (enc && (out_len <= *enc_len))
389 {
390 /* combine header and digest */
391 memcpy(enc, di->header, di->sz);
392 memcpy(enc + di->sz, tbs, tbslen);
393 dmsg(D_XKEY, "encode_pkcs1: digest length = %zu encoded length = %zu", tbslen, out_len);
394 ret = true;
395 }
396
397done:
398 *enc_len = out_len; /* assignment safe as out_len is > 0 at this point */
399
400 return ret;
401}
402
409int
410ecdsa_bin2der(unsigned char *buf, int len, size_t capacity)
411{
412 ECDSA_SIG *ecsig = NULL;
413 int rlen = len / 2;
414 BIGNUM *r = BN_bin2bn(buf, rlen, NULL);
415 BIGNUM *s = BN_bin2bn(buf + rlen, rlen, NULL);
416 if (!r || !s)
417 {
418 goto err;
419 }
420 ecsig = ECDSA_SIG_new(); /* this does not allocate r, s */
421 if (!ecsig)
422 {
423 goto err;
424 }
425 if (!ECDSA_SIG_set0(ecsig, r, s)) /* ecsig takes ownership of r and s */
426 {
427 ECDSA_SIG_free(ecsig);
428 goto err;
429 }
430
431 int derlen = i2d_ECDSA_SIG(ecsig, NULL);
432 if (derlen > (int)capacity)
433 {
434 ECDSA_SIG_free(ecsig);
435 msg(M_NONFATAL, "Error: DER encoded ECDSA signature is too long (%d)", derlen);
436 return 0;
437 }
438 derlen = i2d_ECDSA_SIG(ecsig, &buf);
439 ECDSA_SIG_free(ecsig);
440 return derlen;
441
442err:
443 BN_free(r); /* it is ok to free NULL BN */
444 BN_free(s);
445 return 0;
446}
447
448#endif /* HAVE_XKEY_PROVIDER */
Buffer management functions and garbage collection.
size_t strlcpy(char *dest, const char *src, size_t maxlen)
size_t strlcat(char *dest, const char *src, size_t maxlen)
#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:3966
#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
static const EVP_MD * EVP_MD_fetch(const void *ctx, const char *algorithm, const char *properties)
void OSSL_LIB_CTX
static void EVP_MD_free(const EVP_MD *md)
#define M_FATAL
Definition error.h:90
#define M_NONFATAL
Definition error.h:91
#define dmsg(flags,...)
Definition error.h:164
#define msg(flags,...)
Definition error.h:152
#define ASSERT(x)
Definition error.h:221
#define M_WARN
Definition error.h:92
int openvpn_base64_decode(const char *str, void *data, int size)
Definition base64.c:160
int openvpn_base64_encode(const void *data, int size, char **str)
Definition base64.c:51
unsigned int flags
Definition manage.h:248
struct man_settings settings
Definition manage.h:345