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