• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19020191358

02 Nov 2025 05:04PM UTC coverage: 72.222% (-0.02%) from 72.241%
19020191358

push

github

web-flow
Enhance docs for ukify and direct kernel boots (#39516)

305246 of 422650 relevant lines covered (72.22%)

1085243.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.43
/src/shared/openssl-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "alloc-util.h"
4
#include "ask-password-api.h"
5
#include "fd-util.h"
6
#include "fileio.h"
7
#include "hexdecoct.h"
8
#include "log.h"
9
#include "memory-util.h"
10
#include "memstream-util.h"
11
#include "openssl-util.h"
12
#include "random-util.h"
13
#include "string-util.h"
14
#include "strv.h"
15

16
#if HAVE_OPENSSL
17
#  include <openssl/ec.h>
18
#  include <openssl/rsa.h>
19

20
#  if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
21
#    include <openssl/engine.h>
22
DISABLE_WARNING_DEPRECATED_DECLARATIONS;
23
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ENGINE*, ENGINE_free, NULL);
×
24
REENABLE_WARNING;
25
#  endif
26

27
#ifndef OPENSSL_NO_UI_CONSOLE
28
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UI_METHOD*, UI_destroy_method, NULL);
×
29
#endif
30

31
/* For each error in the OpenSSL thread error queue, log the provided message and the OpenSSL error
32
 * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No OpenSSL
33
 * errors." This logs at level debug. Returns -EIO (or -ENOMEM). */
34
#define log_openssl_errors(fmt, ...) _log_openssl_errors(UNIQ, fmt, ##__VA_ARGS__)
35
#define _log_openssl_errors(u, fmt, ...)                                \
36
        ({                                                              \
37
                size_t UNIQ_T(MAX, u) = 512 /* arbitrary, but openssl doc states it must be >= 256 */; \
38
                _cleanup_free_ char *UNIQ_T(BUF, u) = malloc(UNIQ_T(MAX, u)); \
39
                !UNIQ_T(BUF, u)                                         \
40
                        ? log_oom_debug()                               \
41
                        : __log_openssl_errors(u, UNIQ_T(BUF, u), UNIQ_T(MAX, u), fmt, ##__VA_ARGS__) \
42
                        ?: log_debug_errno(SYNTHETIC_ERRNO(EIO), fmt ": No OpenSSL errors.", ##__VA_ARGS__); \
43
        })
44
#define __log_openssl_errors(u, buf, max, fmt, ...)                     \
45
        ({                                                              \
46
                int UNIQ_T(R, u) = 0;                                   \
47
                for (;;) {                                              \
48
                        unsigned long UNIQ_T(E, u) = ERR_get_error();   \
49
                        if (UNIQ_T(E, u) == 0)                          \
50
                                break;                                  \
51
                        ERR_error_string_n(UNIQ_T(E, u), buf, max);     \
52
                        UNIQ_T(R, u) = log_debug_errno(SYNTHETIC_ERRNO(EIO), fmt ": %s", ##__VA_ARGS__, buf); \
53
                }                                                       \
54
                UNIQ_T(R, u);                                           \
55
        })
56

57
int openssl_pubkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret) {
121✔
58
        assert(pem);
121✔
59
        assert(ret);
121✔
60

61
        if (pem_size == SIZE_MAX)
121✔
62
                pem_size = strlen(pem);
14✔
63

64
        _cleanup_fclose_ FILE *f = NULL;
121✔
65
        f = fmemopen((void*) pem, pem_size, "r");
121✔
66
        if (!f)
121✔
67
                return log_oom_debug();
×
68

69
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = PEM_read_PUBKEY(f, /* x= */ NULL, /* pam_password_cb= */ NULL, /* userdata= */ NULL);
121✔
70
        if (!pkey)
121✔
71
                return log_openssl_errors("Failed to parse PEM");
4✔
72

73
        *ret = TAKE_PTR(pkey);
120✔
74
        return 0;
120✔
75
}
76

77
int openssl_pubkey_to_pem(EVP_PKEY *pkey, char **ret) {
39✔
78
        assert(pkey);
39✔
79
        assert(ret);
39✔
80

81
        _cleanup_(memstream_done) MemStream m = {};
39✔
82
        FILE *f = memstream_init(&m);
39✔
83
        if (!f)
39✔
84
                return -ENOMEM;
85

86
        if (PEM_write_PUBKEY(f, pkey) <= 0)
39✔
87
                return -EIO;
88

89
        return memstream_finalize(&m, ret, /* ret_size= */ NULL);
39✔
90
}
91

92
/* Returns the number of bytes generated by the specified digest algorithm. This can be used only for
93
 * fixed-size algorithms, e.g. md5, sha1, sha256, etc. Do not use this for variable-sized digest algorithms,
94
 * e.g. shake128. Returns 0 on success, -EOPNOTSUPP if the algorithm is not supported, or < 0 for any other
95
 * error. */
96
int openssl_digest_size(const char *digest_alg, size_t *ret_digest_size) {
60✔
97
        assert(digest_alg);
60✔
98
        assert(ret_digest_size);
60✔
99

100
#if OPENSSL_VERSION_MAJOR >= 3
101
        _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL);
120✔
102
#else
103
        const EVP_MD *md = EVP_get_digestbyname(digest_alg);
104
#endif
105
        if (!md)
60✔
106
                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1✔
107
                                       "Digest algorithm '%s' not supported.", digest_alg);
108

109
        size_t digest_size;
59✔
110
#if OPENSSL_VERSION_MAJOR >= 3
111
        digest_size = EVP_MD_get_size(md);
59✔
112
#else
113
        digest_size = EVP_MD_size(md);
114
#endif
115
        if (digest_size == 0)
59✔
116
                return log_openssl_errors("Failed to get Digest size");
×
117

118
        *ret_digest_size = digest_size;
59✔
119

120
        return 0;
59✔
121
}
122

123
/* Calculate the digest hash value for the provided data, using the specified digest algorithm. Returns 0 on
124
 * success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any other error. */
125
int openssl_digest_many(
19✔
126
                const char *digest_alg,
127
                const struct iovec data[],
128
                size_t n_data,
129
                void **ret_digest,
130
                size_t *ret_digest_size) {
131

132
        int r;
19✔
133

134
        assert(digest_alg);
19✔
135
        assert(data || n_data == 0);
19✔
136
        assert(ret_digest);
19✔
137
        /* ret_digest_size is optional, as caller may already know the digest size */
138

139
#if OPENSSL_VERSION_MAJOR >= 3
140
        _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL);
38✔
141
#else
142
        const EVP_MD *md = EVP_get_digestbyname(digest_alg);
143
#endif
144
        if (!md)
19✔
145
                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
146
                                       "Digest algorithm '%s' not supported.", digest_alg);
147

148
        _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = EVP_MD_CTX_new();
38✔
149
        if (!ctx)
19✔
150
                return log_openssl_errors("Failed to create new EVP_MD_CTX");
×
151

152
        if (!EVP_DigestInit_ex(ctx, md, NULL))
19✔
153
                return log_openssl_errors("Failed to initialize EVP_MD_CTX");
×
154

155
        for (size_t i = 0; i < n_data; i++)
58✔
156
                if (!EVP_DigestUpdate(ctx, data[i].iov_base, data[i].iov_len))
39✔
157
                        return log_openssl_errors("Failed to update Digest");
×
158

159
        size_t digest_size;
19✔
160
        r = openssl_digest_size(digest_alg, &digest_size);
19✔
161
        if (r < 0)
19✔
162
                return r;
163

164
        _cleanup_free_ void *buf = malloc(digest_size);
19✔
165
        if (!buf)
19✔
166
                return log_oom_debug();
×
167

168
        unsigned size;
19✔
169
        if (!EVP_DigestFinal_ex(ctx, buf, &size))
19✔
170
                return log_openssl_errors("Failed to finalize Digest");
×
171

172
        assert(size == digest_size);
19✔
173

174
        *ret_digest = TAKE_PTR(buf);
19✔
175
        if (ret_digest_size)
19✔
176
                *ret_digest_size = size;
19✔
177

178
        return 0;
179
}
180

181
/* Calculate the HMAC digest hash value for the provided data, using the provided key and specified digest
182
 * algorithm. Returns 0 on success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any
183
 * other error. */
184
int openssl_hmac_many(
54✔
185
                const char *digest_alg,
186
                const void *key,
187
                size_t key_size,
188
                const struct iovec data[],
189
                size_t n_data,
190
                void **ret_digest,
191
                size_t *ret_digest_size) {
192

193
        assert(digest_alg);
54✔
194
        assert(key);
54✔
195
        assert(data || n_data == 0);
54✔
196
        assert(ret_digest);
54✔
197
        /* ret_digest_size is optional, as caller may already know the digest size */
198

199
#if OPENSSL_VERSION_MAJOR >= 3
200
        _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL);
108✔
201
#else
202
        const EVP_MD *md = EVP_get_digestbyname(digest_alg);
203
#endif
204
        if (!md)
54✔
205
                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
206
                                       "Digest algorithm '%s' not supported.", digest_alg);
207

208
#if OPENSSL_VERSION_MAJOR >= 3
209
        _cleanup_(EVP_MAC_freep) EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
108✔
210
        if (!mac)
54✔
211
                return log_openssl_errors("Failed to create new EVP_MAC");
×
212

213
        _cleanup_(EVP_MAC_CTX_freep) EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac);
108✔
214
        if (!ctx)
54✔
215
                return log_openssl_errors("Failed to create new EVP_MAC_CTX");
×
216

217
        _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
108✔
218
        if (!bld)
54✔
219
                return log_openssl_errors("Failed to create new OSSL_PARAM_BLD");
×
220

221
        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_MAC_PARAM_DIGEST, (char*) digest_alg, 0))
54✔
222
                return log_openssl_errors("Failed to set HMAC OSSL_MAC_PARAM_DIGEST");
×
223

224
        _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
108✔
225
        if (!params)
54✔
226
                return log_openssl_errors("Failed to build HMAC OSSL_PARAM");
×
227

228
        if (!EVP_MAC_init(ctx, key, key_size, params))
54✔
229
                return log_openssl_errors("Failed to initialize EVP_MAC_CTX");
×
230
#else
231
        _cleanup_(HMAC_CTX_freep) HMAC_CTX *ctx = HMAC_CTX_new();
232
        if (!ctx)
233
                return log_openssl_errors("Failed to create new HMAC_CTX");
234

235
        if (!HMAC_Init_ex(ctx, key, key_size, md, NULL))
236
                return log_openssl_errors("Failed to initialize HMAC_CTX");
237
#endif
238

239
        for (size_t i = 0; i < n_data; i++)
123✔
240
#if OPENSSL_VERSION_MAJOR >= 3
241
                if (!EVP_MAC_update(ctx, data[i].iov_base, data[i].iov_len))
69✔
242
#else
243
                if (!HMAC_Update(ctx, data[i].iov_base, data[i].iov_len))
244
#endif
245
                        return log_openssl_errors("Failed to update HMAC");
×
246

247
        size_t digest_size;
54✔
248
#if OPENSSL_VERSION_MAJOR >= 3
249
        digest_size = EVP_MAC_CTX_get_mac_size(ctx);
54✔
250
#else
251
        digest_size = HMAC_size(ctx);
252
#endif
253
        if (digest_size == 0)
54✔
254
                return log_openssl_errors("Failed to get HMAC digest size");
×
255

256
        _cleanup_free_ void *buf = malloc(digest_size);
54✔
257
        if (!buf)
54✔
258
                return log_oom_debug();
×
259

260
#if OPENSSL_VERSION_MAJOR >= 3
261
        size_t size;
54✔
262
        if (!EVP_MAC_final(ctx, buf, &size, digest_size))
54✔
263
#else
264
        unsigned size;
265
        if (!HMAC_Final(ctx, buf, &size))
266
#endif
267
                return log_openssl_errors("Failed to finalize HMAC");
×
268

269
        assert(size == digest_size);
54✔
270

271
        *ret_digest = TAKE_PTR(buf);
54✔
272
        if (ret_digest_size)
54✔
273
                *ret_digest_size = size;
54✔
274

275
        return 0;
276
}
277

278
/* Symmetric Cipher encryption using the alg-bits-mode cipher, e.g. AES-128-CFB. The key is required and must
279
 * be at least the minimum required key length for the cipher. The IV is optional but, if provided, it must
280
 * be at least the minimum iv length for the cipher. If no IV is provided and the cipher requires one, a
281
 * buffer of zeroes is used. Returns 0 on success, -EOPNOTSUPP if the cipher algorithm is not supported, or <
282
 * 0 on any other error. */
283
int openssl_cipher_many(
17✔
284
                const char *alg,
285
                size_t bits,
286
                const char *mode,
287
                const void *key,
288
                size_t key_size,
289
                const void *iv,
290
                size_t iv_size,
291
                const struct iovec data[],
292
                size_t n_data,
293
                void **ret,
294
                size_t *ret_size) {
295

296
        assert(alg);
17✔
297
        assert(bits > 0);
17✔
298
        assert(mode);
17✔
299
        assert(key);
17✔
300
        assert(iv || iv_size == 0);
17✔
301
        assert(data || n_data == 0);
17✔
302
        assert(ret);
17✔
303
        assert(ret_size);
17✔
304

305
        _cleanup_free_ char *cipher_alg = NULL;
17✔
306
        if (asprintf(&cipher_alg, "%s-%zu-%s", alg, bits, mode) < 0)
17✔
307
                return log_oom_debug();
×
308

309
#if OPENSSL_VERSION_MAJOR >= 3
310
        _cleanup_(EVP_CIPHER_freep) EVP_CIPHER *cipher = EVP_CIPHER_fetch(NULL, cipher_alg, NULL);
34✔
311
#else
312
        const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_alg);
313
#endif
314
        if (!cipher)
17✔
315
                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
316
                                       "Cipher algorithm '%s' not supported.", cipher_alg);
317

318
        _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
34✔
319
        if (!ctx)
17✔
320
                return log_openssl_errors("Failed to create new EVP_CIPHER_CTX");
×
321

322
        /* Verify enough key data was provided. */
323
        int cipher_key_length = EVP_CIPHER_key_length(cipher);
17✔
324
        assert(cipher_key_length >= 0);
17✔
325
        if ((size_t) cipher_key_length > key_size)
17✔
326
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
327
                                       "Not enough key bytes provided, require %d", cipher_key_length);
328

329
        /* Verify enough IV data was provided or, if no IV was provided, use a zeroed buffer for IV data. */
330
        int cipher_iv_length = EVP_CIPHER_iv_length(cipher);
17✔
331
        assert(cipher_iv_length >= 0);
17✔
332
        _cleanup_free_ void *zero_iv = NULL;
17✔
333
        if (iv_size == 0) {
17✔
334
                zero_iv = malloc0(cipher_iv_length);
11✔
335
                if (!zero_iv)
11✔
336
                        return log_oom_debug();
×
337

338
                iv = zero_iv;
11✔
339
                iv_size = (size_t) cipher_iv_length;
11✔
340
        }
341
        if ((size_t) cipher_iv_length > iv_size)
17✔
342
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
343
                                       "Not enough IV bytes provided, require %d", cipher_iv_length);
344

345
        if (!EVP_EncryptInit(ctx, cipher, key, iv))
17✔
346
                return log_openssl_errors("Failed to initialize EVP_CIPHER_CTX.");
×
347

348
        int cipher_block_size = EVP_CIPHER_CTX_block_size(ctx);
17✔
349
        assert(cipher_block_size > 0);
17✔
350

351
        _cleanup_free_ uint8_t *buf = NULL;
17✔
352
        size_t size = 0;
17✔
353

354
        for (size_t i = 0; i < n_data; i++) {
61✔
355
                /* Cipher may produce (up to) input length + cipher block size of output. */
356
                if (!GREEDY_REALLOC(buf, size + data[i].iov_len + cipher_block_size))
44✔
357
                        return log_oom_debug();
×
358

359
                int update_size;
44✔
360
                if (!EVP_EncryptUpdate(ctx, &buf[size], &update_size, data[i].iov_base, data[i].iov_len))
44✔
361
                        return log_openssl_errors("Failed to update Cipher.");
×
362

363
                size += update_size;
44✔
364
        }
365

366
        if (!GREEDY_REALLOC(buf, size + cipher_block_size))
17✔
367
                return log_oom_debug();
×
368

369
        int final_size;
17✔
370
        if (!EVP_EncryptFinal_ex(ctx, &buf[size], &final_size))
17✔
371
                return log_openssl_errors("Failed to finalize Cipher.");
×
372

373
        *ret = TAKE_PTR(buf);
17✔
374
        *ret_size = size + final_size;
17✔
375

376
        return 0;
17✔
377
}
378

379
/* Perform Single-Step (aka "Concat") KDF. Currently, this only supports using the digest for the auxiliary
380
 * function. The derive_size parameter specifies how many bytes are derived.
381
 *
382
 * For more details see: https://www.openssl.org/docs/manmaster/man7/EVP_KDF-SS.html */
383
int kdf_ss_derive(
6✔
384
                const char *digest,
385
                const void *key,
386
                size_t key_size,
387
                const void *salt,
388
                size_t salt_size,
389
                const void *info,
390
                size_t info_size,
391
                size_t derive_size,
392
                void **ret) {
393

394
#if OPENSSL_VERSION_MAJOR >= 3
395
        assert(digest);
6✔
396
        assert(key);
6✔
397
        assert(derive_size > 0);
6✔
398
        assert(ret);
6✔
399

400
        _cleanup_(EVP_KDF_freep) EVP_KDF *kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL);
12✔
401
        if (!kdf)
6✔
402
                return log_openssl_errors("Failed to create new EVP_KDF");
×
403

404
        _cleanup_(EVP_KDF_CTX_freep) EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
12✔
405
        if (!ctx)
6✔
406
                return log_openssl_errors("Failed to create new EVP_KDF_CTX");
×
407

408
        _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
12✔
409
        if (!bld)
6✔
410
                return log_openssl_errors("Failed to create new OSSL_PARAM_BLD");
×
411

412
        _cleanup_free_ void *buf = malloc(derive_size);
6✔
413
        if (!buf)
6✔
414
                return log_oom_debug();
×
415

416
        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_DIGEST, (char*) digest, 0))
6✔
417
                return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_DIGEST");
×
418

419
        if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_KEY, (char*) key, key_size))
6✔
420
                return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_KEY");
×
421

422
        if (salt)
6✔
423
                if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SALT, (char*) salt, salt_size))
3✔
424
                        return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_SALT");
×
425

426
        if (info)
6✔
427
                if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_INFO, (char*) info, info_size))
6✔
428
                        return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_INFO");
×
429

430
        _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
12✔
431
        if (!params)
6✔
432
                return log_openssl_errors("Failed to build KDF-SS OSSL_PARAM");
×
433

434
        if (EVP_KDF_derive(ctx, buf, derive_size, params) <= 0)
6✔
435
                return log_openssl_errors("OpenSSL KDF-SS derive failed");
×
436

437
        *ret = TAKE_PTR(buf);
6✔
438

439
        return 0;
6✔
440
#else
441
        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "KDF-SS requires OpenSSL >= 3.");
442
#endif
443
}
444

445
/* Perform Key-Based HMAC KDF. The mode must be "COUNTER" or "FEEDBACK". The parameter naming is from the
446
 * OpenSSL api, and maps to SP800-108 naming as "...key, salt, info, and seed correspond to KI, Label,
447
 * Context, and IV (respectively)...". The derive_size parameter specifies how many bytes are derived.
448
 *
449
 * For more details see: https://www.openssl.org/docs/manmaster/man7/EVP_KDF-KB.html */
450
int kdf_kb_hmac_derive(
7✔
451
                const char *mode,
452
                const char *digest,
453
                const void *key,
454
                size_t key_size,
455
                const void *salt,
456
                size_t salt_size,
457
                const void *info,
458
                size_t info_size,
459
                const void *seed,
460
                size_t seed_size,
461
                size_t derive_size,
462
                void **ret) {
463

464
#if OPENSSL_VERSION_MAJOR >= 3
465
        assert(mode);
7✔
466
        assert(strcaseeq(mode, "COUNTER") || strcaseeq(mode, "FEEDBACK"));
7✔
467
        assert(digest);
7✔
468
        assert(key || key_size == 0);
7✔
469
        assert(salt || salt_size == 0);
7✔
470
        assert(info || info_size == 0);
7✔
471
        assert(seed || seed_size == 0);
7✔
472
        assert(derive_size > 0);
7✔
473
        assert(ret);
7✔
474

475
        _cleanup_(EVP_KDF_freep) EVP_KDF *kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL);
14✔
476
        if (!kdf)
7✔
477
                return log_openssl_errors("Failed to create new EVP_KDF");
×
478

479
        _cleanup_(EVP_KDF_CTX_freep) EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
14✔
480
        if (!ctx)
7✔
481
                return log_openssl_errors("Failed to create new EVP_KDF_CTX");
×
482

483
        _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
14✔
484
        if (!bld)
7✔
485
                return log_openssl_errors("Failed to create new OSSL_PARAM_BLD");
×
486

487
        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_MAC, (char*) "HMAC", 0))
7✔
488
                return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_MAC");
×
489

490
        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_MODE, (char*) mode, 0))
7✔
491
                return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_MODE");
×
492

493
        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_DIGEST, (char*) digest, 0))
7✔
494
                return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_DIGEST");
×
495

496
        if (key)
7✔
497
                if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_KEY, (char*) key, key_size))
7✔
498
                        return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_KEY");
×
499

500
        if (salt)
7✔
501
                if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SALT, (char*) salt, salt_size))
7✔
502
                        return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_SALT");
×
503

504
        if (info)
7✔
505
                if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_INFO, (char*) info, info_size))
4✔
506
                        return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_INFO");
×
507

508
        if (seed)
7✔
509
                if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SEED, (char*) seed, seed_size))
×
510
                        return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_SEED");
×
511

512
        _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
14✔
513
        if (!params)
7✔
514
                return log_openssl_errors("Failed to build KDF-KB OSSL_PARAM");
×
515

516
        _cleanup_free_ void *buf = malloc(derive_size);
7✔
517
        if (!buf)
7✔
518
                return log_oom_debug();
×
519

520
        if (EVP_KDF_derive(ctx, buf, derive_size, params) <= 0)
7✔
521
                return log_openssl_errors("OpenSSL KDF-KB derive failed");
×
522

523
        *ret = TAKE_PTR(buf);
7✔
524

525
        return 0;
7✔
526
#else
527
        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "KDF-KB requires OpenSSL >= 3.");
528
#endif
529
}
530

531
int rsa_encrypt_bytes(
3✔
532
                EVP_PKEY *pkey,
533
                const void *decrypted_key,
534
                size_t decrypted_key_size,
535
                void **ret_encrypt_key,
536
                size_t *ret_encrypt_key_size) {
537

538
        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL;
3✔
539
        _cleanup_free_ void *b = NULL;
3✔
540
        size_t l;
3✔
541

542
        ctx = EVP_PKEY_CTX_new(pkey, NULL);
3✔
543
        if (!ctx)
3✔
544
                return log_openssl_errors("Failed to allocate public key context");
×
545

546
        if (EVP_PKEY_encrypt_init(ctx) <= 0)
3✔
547
                return log_openssl_errors("Failed to initialize public key context");
×
548

549
        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)
3✔
550
                return log_openssl_errors("Failed to configure PKCS#1 padding");
×
551

552
        if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0)
3✔
553
                return log_openssl_errors("Failed to determine encrypted key size");
×
554

555
        b = malloc(l);
3✔
556
        if (!b)
3✔
557
                return -ENOMEM;
558

559
        if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0)
3✔
560
                return log_openssl_errors("Failed to determine encrypted key size");
×
561

562
        *ret_encrypt_key = TAKE_PTR(b);
3✔
563
        *ret_encrypt_key_size = l;
3✔
564
        return 0;
3✔
565
}
566

567
/* Encrypt the key data using RSA-OAEP with the provided label and specified digest algorithm. Returns 0 on
568
 * success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any other error. */
569
int rsa_oaep_encrypt_bytes(
×
570
                const EVP_PKEY *pkey,
571
                const char *digest_alg,
572
                const char *label,
573
                const void *decrypted_key,
574
                size_t decrypted_key_size,
575
                void **ret_encrypt_key,
576
                size_t *ret_encrypt_key_size) {
577

578
        assert(pkey);
×
579
        assert(digest_alg);
×
580
        assert(label);
×
581
        assert(decrypted_key);
×
582
        assert(decrypted_key_size > 0);
×
583
        assert(ret_encrypt_key);
×
584
        assert(ret_encrypt_key_size);
×
585

586
#if OPENSSL_VERSION_MAJOR >= 3
587
        _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL);
×
588
#else
589
        const EVP_MD *md = EVP_get_digestbyname(digest_alg);
590
#endif
591
        if (!md)
×
592
                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
593
                                       "Digest algorithm '%s' not supported.", digest_alg);
594

595
        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) pkey, NULL);
×
596
        if (!ctx)
×
597
                return log_openssl_errors("Failed to create new EVP_PKEY_CTX");
×
598

599
        if (EVP_PKEY_encrypt_init(ctx) <= 0)
×
600
                return log_openssl_errors("Failed to initialize EVP_PKEY_CTX");
×
601

602
        if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0)
×
603
                return log_openssl_errors("Failed to configure RSA-OAEP padding");
×
604

605
        if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0)
×
606
                return log_openssl_errors("Failed to configure RSA-OAEP MD");
×
607

608
        _cleanup_free_ char *duplabel = strdup(label);
×
609
        if (!duplabel)
×
610
                return log_oom_debug();
×
611

612
        if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, duplabel, strlen(duplabel) + 1) <= 0)
×
613
                return log_openssl_errors("Failed to configure RSA-OAEP label");
×
614
        /* ctx owns this now, don't free */
615
        TAKE_PTR(duplabel);
×
616

617
        size_t size = 0;
×
618
        if (EVP_PKEY_encrypt(ctx, NULL, &size, decrypted_key, decrypted_key_size) <= 0)
×
619
                return log_openssl_errors("Failed to determine RSA-OAEP encrypted key size");
×
620

621
        _cleanup_free_ void *buf = malloc(size);
×
622
        if (!buf)
×
623
                return log_oom_debug();
×
624

625
        if (EVP_PKEY_encrypt(ctx, buf, &size, decrypted_key, decrypted_key_size) <= 0)
×
626
                return log_openssl_errors("Failed to RSA-OAEP encrypt");
×
627

628
        *ret_encrypt_key = TAKE_PTR(buf);
×
629
        *ret_encrypt_key_size = size;
×
630

631
        return 0;
×
632
}
633

634
int rsa_pkey_to_suitable_key_size(
3✔
635
                EVP_PKEY *pkey,
636
                size_t *ret_suitable_key_size) {
637

638
        size_t suitable_key_size;
3✔
639
        int bits;
3✔
640

641
        assert(pkey);
3✔
642
        assert(ret_suitable_key_size);
3✔
643

644
        /* Analyzes the specified public key and that it is RSA. If so, will return a suitable size for a
645
         * disk encryption key to encrypt with RSA for use in PKCS#11 security token schemes. */
646

647
        if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA)
3✔
648
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key.");
×
649

650
        bits = EVP_PKEY_bits(pkey);
3✔
651
        log_debug("Bits in RSA key: %i", bits);
3✔
652

653
        /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only
654
         * generate a random key half the size of the RSA length */
655
        suitable_key_size = bits / 8 / 2;
3✔
656

657
        if (suitable_key_size < 1)
3✔
658
                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?");
×
659

660
        *ret_suitable_key_size = suitable_key_size;
3✔
661
        return 0;
3✔
662
}
663

664
/* Generate RSA public key from provided "n" and "e" values. Numbers "n" and "e" must be provided here
665
 * in big-endian format, e.g. wrap it with htobe32() for uint32_t. */
666
int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size, EVP_PKEY **ret) {
48✔
667
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
48✔
668

669
        assert(n);
48✔
670
        assert(n_size != 0);
48✔
671
        assert(e);
48✔
672
        assert(e_size != 0);
48✔
673
        assert(ret);
48✔
674

675
#if OPENSSL_VERSION_MAJOR >= 3
676
        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
96✔
677
        if (!ctx)
48✔
678
                return log_openssl_errors("Failed to create new EVP_PKEY_CTX");
×
679

680
        if (EVP_PKEY_fromdata_init(ctx) <= 0)
48✔
681
                return log_openssl_errors("Failed to initialize EVP_PKEY_CTX");
×
682

683
        OSSL_PARAM params[3];
48✔
684

685
#if __BYTE_ORDER == __BIG_ENDIAN
686
        params[0] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, (void*)n, n_size);
687
        params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, (void*)e, e_size);
688
#else
689
        _cleanup_free_ void *native_n = memdup_reverse(n, n_size);
96✔
690
        if (!native_n)
48✔
691
                return log_oom_debug();
×
692

693
        _cleanup_free_ void *native_e = memdup_reverse(e, e_size);
96✔
694
        if (!native_e)
48✔
695
                return log_oom_debug();
×
696

697
        params[0] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, native_n, n_size);
48✔
698
        params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, native_e, e_size);
48✔
699
#endif
700
        params[2] = OSSL_PARAM_construct_end();
48✔
701

702
        if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0)
48✔
703
                return log_openssl_errors("Failed to create RSA EVP_PKEY");
×
704
#else
705
        _cleanup_(BN_freep) BIGNUM *bn_n = BN_bin2bn(n, n_size, NULL);
706
        if (!bn_n)
707
                return log_openssl_errors("Failed to create BIGNUM for RSA n");
708

709
        _cleanup_(BN_freep) BIGNUM *bn_e = BN_bin2bn(e, e_size, NULL);
710
        if (!bn_e)
711
                return log_openssl_errors("Failed to create BIGNUM for RSA e");
712

713
        _cleanup_(RSA_freep) RSA *rsa_key = RSA_new();
714
        if (!rsa_key)
715
                return log_openssl_errors("Failed to create new RSA");
716

717
        if (!RSA_set0_key(rsa_key, bn_n, bn_e, NULL))
718
                return log_openssl_errors("Failed to set RSA n/e");
719
        /* rsa_key owns these now, don't free */
720
        TAKE_PTR(bn_n);
721
        TAKE_PTR(bn_e);
722

723
        pkey = EVP_PKEY_new();
724
        if (!pkey)
725
                return log_openssl_errors("Failed to create new EVP_PKEY");
726

727
        if (!EVP_PKEY_assign_RSA(pkey, rsa_key))
728
                return log_openssl_errors("Failed to assign RSA key");
729
        /* pkey owns this now, don't free */
730
        TAKE_PTR(rsa_key);
731
#endif
732

733
        *ret = TAKE_PTR(pkey);
48✔
734

735
        return 0;
48✔
736
}
737

738
/* Get the "n" and "e" values from the pkey. The values are returned in "bin" format, i.e. BN_bn2bin(). */
739
int rsa_pkey_to_n_e(
80✔
740
                const EVP_PKEY *pkey,
741
                void **ret_n,
742
                size_t *ret_n_size,
743
                void **ret_e,
744
                size_t *ret_e_size) {
745

746
        assert(pkey);
80✔
747
        assert(ret_n);
80✔
748
        assert(ret_n_size);
80✔
749
        assert(ret_e);
80✔
750
        assert(ret_e_size);
80✔
751

752
#if OPENSSL_VERSION_MAJOR >= 3
753
        _cleanup_(BN_freep) BIGNUM *bn_n = NULL;
80✔
754
        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &bn_n))
80✔
755
                return log_openssl_errors("Failed to get RSA n");
×
756

757
        _cleanup_(BN_freep) BIGNUM *bn_e = NULL;
80✔
758
        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &bn_e))
80✔
759
                return log_openssl_errors("Failed to get RSA e");
×
760
#else
761
        const RSA *rsa = EVP_PKEY_get0_RSA((EVP_PKEY*) pkey);
762
        if (!rsa)
763
                return log_openssl_errors("Failed to get RSA key from public key");
764

765
        const BIGNUM *bn_n = RSA_get0_n(rsa);
766
        if (!bn_n)
767
                return log_openssl_errors("Failed to get RSA n");
768

769
        const BIGNUM *bn_e = RSA_get0_e(rsa);
770
        if (!bn_e)
771
                return log_openssl_errors("Failed to get RSA e");
772
#endif
773

774
        size_t n_size = BN_num_bytes(bn_n), e_size = BN_num_bytes(bn_e);
80✔
775
        _cleanup_free_ void *n = malloc(n_size), *e = malloc(e_size);
80✔
776
        if (!n || !e)
80✔
777
                return log_oom_debug();
×
778

779
        assert(BN_bn2bin(bn_n, n) == (int) n_size);
80✔
780
        assert(BN_bn2bin(bn_e, e) == (int) e_size);
80✔
781

782
        *ret_n = TAKE_PTR(n);
80✔
783
        *ret_n_size = n_size;
80✔
784
        *ret_e = TAKE_PTR(e);
80✔
785
        *ret_e_size = e_size;
80✔
786

787
        return 0;
80✔
788
}
789

790
/* Generate ECC public key from provided curve ID and x/y points. */
791
int ecc_pkey_from_curve_x_y(
19✔
792
                int curve_id,
793
                const void *x,
794
                size_t x_size,
795
                const void *y,
796
                size_t y_size,
797
                EVP_PKEY **ret) {
798

799
        assert(x);
19✔
800
        assert(y);
19✔
801
        assert(ret);
19✔
802

803
        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
38✔
804
        if (!ctx)
19✔
805
                return log_openssl_errors("Failed to create new EVP_PKEY_CTX");
×
806

807
        _cleanup_(BN_freep) BIGNUM *bn_x = BN_bin2bn(x, x_size, NULL);
38✔
808
        if (!bn_x)
19✔
809
                return log_openssl_errors("Failed to create BIGNUM x");
×
810

811
        _cleanup_(BN_freep) BIGNUM *bn_y = BN_bin2bn(y, y_size, NULL);
38✔
812
        if (!bn_y)
19✔
813
                return log_openssl_errors("Failed to create BIGNUM y");
×
814

815
        _cleanup_(EC_GROUP_freep) EC_GROUP *group = EC_GROUP_new_by_curve_name(curve_id);
38✔
816
        if (!group)
19✔
817
                return log_openssl_errors("ECC curve id %d not supported", curve_id);
×
818

819
        _cleanup_(EC_POINT_freep) EC_POINT *point = EC_POINT_new(group);
38✔
820
        if (!point)
19✔
821
                return log_openssl_errors("Failed to create new EC_POINT");
×
822

823
        if (!EC_POINT_set_affine_coordinates(group, point, bn_x, bn_y, NULL))
19✔
824
                return log_openssl_errors("Failed to set ECC coordinates");
×
825

826
#if OPENSSL_VERSION_MAJOR >= 3
827
        if (EVP_PKEY_fromdata_init(ctx) <= 0)
19✔
828
                return log_openssl_errors("Failed to initialize EVP_PKEY_CTX");
×
829

830
        _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
38✔
831
        if (!bld)
19✔
832
                return log_openssl_errors("Failed to create new OSSL_PARAM_BLD");
×
833

834
        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, (char*) OSSL_EC_curve_nid2name(curve_id), 0))
19✔
835
                return log_openssl_errors("Failed to add ECC OSSL_PKEY_PARAM_GROUP_NAME");
×
836

837
        _cleanup_(OPENSSL_freep) void *pbuf = NULL;
×
838
        size_t pbuf_len = 0;
19✔
839
        pbuf_len = EC_POINT_point2buf(group, point, POINT_CONVERSION_UNCOMPRESSED, (unsigned char**) &pbuf, NULL);
19✔
840
        if (pbuf_len == 0)
19✔
841
                return log_openssl_errors("Failed to convert ECC point to buffer");
×
842

843
        if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pbuf, pbuf_len))
19✔
844
                return log_openssl_errors("Failed to add ECC OSSL_PKEY_PARAM_PUB_KEY");
×
845

846
        _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld);
38✔
847
        if (!params)
19✔
848
                return log_openssl_errors("Failed to build ECC OSSL_PARAM");
×
849

850
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
19✔
851
        if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0)
19✔
852
                return log_openssl_errors("Failed to create ECC EVP_PKEY");
×
853
#else
854
        _cleanup_(EC_KEY_freep) EC_KEY *eckey = EC_KEY_new();
855
        if (!eckey)
856
                return log_openssl_errors("Failed to create new EC_KEY");
857

858
        if (!EC_KEY_set_group(eckey, group))
859
                return log_openssl_errors("Failed to set ECC group");
860

861
        if (!EC_KEY_set_public_key(eckey, point))
862
                return log_openssl_errors("Failed to set ECC point");
863

864
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = EVP_PKEY_new();
865
        if (!pkey)
866
                return log_openssl_errors("Failed to create new EVP_PKEY");
867

868
        if (!EVP_PKEY_assign_EC_KEY(pkey, eckey))
869
                return log_openssl_errors("Failed to assign ECC key");
870
        /* pkey owns this now, don't free */
871
        TAKE_PTR(eckey);
872
#endif
873

874
        *ret = TAKE_PTR(pkey);
19✔
875
        return 0;
19✔
876
}
877

878
int ecc_pkey_to_curve_x_y(
10✔
879
                const EVP_PKEY *pkey,
880
                int *ret_curve_id,
881
                void **ret_x,
882
                size_t *ret_x_size,
883
                void **ret_y,
884
                size_t *ret_y_size) {
885

886
        _cleanup_(BN_freep) BIGNUM *bn_x = NULL, *bn_y = NULL;
20✔
887
        int curve_id;
10✔
888

889
        assert(pkey);
10✔
890

891
#if OPENSSL_VERSION_MAJOR >= 3
892
        size_t name_size;
10✔
893
        if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0, &name_size))
10✔
894
                return log_openssl_errors("Failed to get ECC group name size");
×
895

896
        _cleanup_free_ char *name = new(char, name_size + 1);
20✔
897
        if (!name)
10✔
898
                return log_oom_debug();
×
899

900
        if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, name, name_size + 1, NULL))
10✔
901
                return log_openssl_errors("Failed to get ECC group name");
×
902

903
        curve_id = OBJ_sn2nid(name);
10✔
904
        if (curve_id == NID_undef)
10✔
905
                return log_openssl_errors("Failed to get ECC curve id");
×
906

907
        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &bn_x))
10✔
908
                return log_openssl_errors("Failed to get ECC point x");
×
909

910
        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &bn_y))
10✔
911
                return log_openssl_errors("Failed to get ECC point y");
×
912
#else
913
        const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY*) pkey);
914
        if (!eckey)
915
                return log_openssl_errors("Failed to get EC_KEY");
916

917
        const EC_GROUP *group = EC_KEY_get0_group(eckey);
918
        if (!group)
919
                return log_openssl_errors("Failed to get EC_GROUP");
920

921
        curve_id = EC_GROUP_get_curve_name(group);
922
        if (curve_id == NID_undef)
923
                return log_openssl_errors("Failed to get ECC curve id");
924

925
        const EC_POINT *point = EC_KEY_get0_public_key(eckey);
926
        if (!point)
927
                return log_openssl_errors("Failed to get EC_POINT");
928

929
        bn_x = BN_new();
930
        bn_y = BN_new();
931
        if (!bn_x || !bn_y)
932
                return log_openssl_errors("Failed to create new BIGNUM");
933

934
        if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y, NULL))
935
                return log_openssl_errors("Failed to get ECC x/y.");
936
#endif
937

938
        size_t x_size = BN_num_bytes(bn_x), y_size = BN_num_bytes(bn_y);
10✔
939
        _cleanup_free_ void *x = malloc(x_size), *y = malloc(y_size);
10✔
940
        if (!x || !y)
10✔
941
                return log_oom_debug();
×
942

943
        assert(BN_bn2bin(bn_x, x) == (int) x_size);
10✔
944
        assert(BN_bn2bin(bn_y, y) == (int) y_size);
10✔
945

946
        if (ret_curve_id)
10✔
947
                *ret_curve_id = curve_id;
7✔
948
        if (ret_x)
10✔
949
                *ret_x = TAKE_PTR(x);
7✔
950
        if (ret_x_size)
10✔
951
                *ret_x_size = x_size;
7✔
952
        if (ret_y)
10✔
953
                *ret_y = TAKE_PTR(y);
7✔
954
        if (ret_y_size)
10✔
955
                *ret_y_size = y_size;
7✔
956

957
        return 0;
958
}
959

960
/* Generate a new ECC key for the specified ECC curve id. */
961
int ecc_pkey_new(int curve_id, EVP_PKEY **ret) {
9✔
962
        assert(ret);
9✔
963

964
        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
18✔
965
        if (!ctx)
9✔
966
                return log_openssl_errors("Failed to create new EVP_PKEY_CTX");
×
967

968
        if (EVP_PKEY_keygen_init(ctx) <= 0)
9✔
969
                return log_openssl_errors("Failed to initialize EVP_PKEY_CTX");
×
970

971
        if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curve_id) <= 0)
9✔
972
                return log_openssl_errors("Failed to set ECC curve %d", curve_id);
×
973

974
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL;
9✔
975
        if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
9✔
976
                return log_openssl_errors("Failed to generate ECC key");
×
977

978
        *ret = TAKE_PTR(pkey);
9✔
979

980
        return 0;
9✔
981
}
982

983
/* Perform ECDH to derive an ECC shared secret between the provided private key and public peer key. For two
984
 * keys, this will result in the same shared secret in either direction; ECDH using Alice's private key and
985
 * Bob's public (peer) key will result in the same shared secret as ECDH using Bob's private key and Alice's
986
 * public (peer) key. On success, this returns 0 and provides the shared secret; otherwise this returns an
987
 * error. */
988
int ecc_ecdh(const EVP_PKEY *private_pkey,
10✔
989
             const EVP_PKEY *peer_pkey,
990
             void **ret_shared_secret,
991
             size_t *ret_shared_secret_size) {
992

993
        assert(private_pkey);
10✔
994
        assert(peer_pkey);
10✔
995
        assert(ret_shared_secret);
10✔
996
        assert(ret_shared_secret_size);
10✔
997

998
        _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) private_pkey, NULL);
20✔
999
        if (!ctx)
10✔
1000
                return log_openssl_errors("Failed to create new EVP_PKEY_CTX");
×
1001

1002
        if (EVP_PKEY_derive_init(ctx) <= 0)
10✔
1003
                return log_openssl_errors("Failed to initialize EVP_PKEY_CTX");
×
1004

1005
        if (EVP_PKEY_derive_set_peer(ctx, (EVP_PKEY*) peer_pkey) <= 0)
10✔
1006
                return log_openssl_errors("Failed to set ECC derive peer");
×
1007

1008
        size_t shared_secret_size;
10✔
1009
        if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0)
10✔
1010
                return log_openssl_errors("Failed to get ECC shared secret size");
×
1011

1012
        _cleanup_(erase_and_freep) void *shared_secret = malloc(shared_secret_size);
10✔
1013
        if (!shared_secret)
10✔
1014
                return log_oom_debug();
×
1015

1016
        if (EVP_PKEY_derive(ctx, (unsigned char*) shared_secret, &shared_secret_size) <= 0)
10✔
1017
                return log_openssl_errors("Failed to derive ECC shared secret");
×
1018

1019
        *ret_shared_secret = TAKE_PTR(shared_secret);
10✔
1020
        *ret_shared_secret_size = shared_secret_size;
10✔
1021

1022
        return 0;
10✔
1023
}
1024

1025
int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) {
85✔
1026
        _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL;
85✔
1027
        _cleanup_free_ void *d = NULL, *h = NULL;
85✔
1028
        int sz, lsz, msz;
85✔
1029
        unsigned umsz;
85✔
1030
        unsigned char *dd;
85✔
1031

1032
        /* Calculates a message digest of the DER encoded public key */
1033

1034
        assert(pk);
85✔
1035
        assert(md);
85✔
1036
        assert(ret);
85✔
1037
        assert(ret_size);
85✔
1038

1039
        sz = i2d_PublicKey(pk, NULL);
85✔
1040
        if (sz < 0)
85✔
1041
                return log_openssl_errors("Unable to convert public key to DER format");
×
1042

1043
        dd = d = malloc(sz);
85✔
1044
        if (!d)
85✔
1045
                return log_oom_debug();
×
1046

1047
        lsz = i2d_PublicKey(pk, &dd);
85✔
1048
        if (lsz < 0)
85✔
1049
                return log_openssl_errors("Unable to convert public key to DER format");
×
1050

1051
        m = EVP_MD_CTX_new();
85✔
1052
        if (!m)
85✔
1053
                return log_openssl_errors("Failed to create new EVP_MD_CTX");
×
1054

1055
        if (EVP_DigestInit_ex(m, md, NULL) != 1)
85✔
1056
                return log_openssl_errors("Failed to initialize %s context", EVP_MD_name(md));
×
1057

1058
        if (EVP_DigestUpdate(m, d, lsz) != 1)
85✔
1059
                return log_openssl_errors("Failed to run %s context", EVP_MD_name(md));
×
1060

1061
        msz = EVP_MD_size(md);
85✔
1062
        assert(msz > 0);
85✔
1063

1064
        h = malloc(msz);
85✔
1065
        if (!h)
85✔
1066
                return log_oom_debug();
×
1067

1068
        umsz = msz;
85✔
1069
        if (EVP_DigestFinal_ex(m, h, &umsz) != 1)
85✔
1070
                return log_openssl_errors("Failed to finalize hash context");
×
1071

1072
        assert(umsz == (unsigned) msz);
85✔
1073

1074
        *ret = TAKE_PTR(h);
85✔
1075
        *ret_size = msz;
85✔
1076

1077
        return 0;
85✔
1078
}
1079

1080
int digest_and_sign(
26✔
1081
                const EVP_MD *md,
1082
                EVP_PKEY *privkey,
1083
                const void *data, size_t size,
1084
                void **ret, size_t *ret_size) {
1085

1086
        int r;
26✔
1087

1088
        assert(privkey);
26✔
1089
        assert(ret);
26✔
1090
        assert(ret_size);
26✔
1091

1092
        if (size == 0)
26✔
1093
                data = ""; /* make sure to pass a valid pointer to OpenSSL */
1094
        else {
1095
                assert(data);
26✔
1096

1097
                if (size == SIZE_MAX) /* If SIZE_MAX input is a string whose size we determine automatically */
26✔
1098
                        size = strlen(data);
24✔
1099
        }
1100

1101
        _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
52✔
1102
        if (!mdctx)
26✔
1103
                return log_openssl_errors("Failed to create new EVP_MD_CTX");
×
1104

1105
        if (EVP_DigestSignInit(mdctx, NULL, md, NULL, privkey) != 1) {
26✔
1106
                /* Distro security policies often disable support for SHA-1. Let's return a recognizable
1107
                 * error for that case. */
1108
                bool invalid_digest = ERR_GET_REASON(ERR_peek_last_error()) == EVP_R_INVALID_DIGEST;
×
1109
                r = log_openssl_errors("Failed to initialize signature context");
×
1110
                return invalid_digest ? -EADDRNOTAVAIL : r;
×
1111
}
1112

1113
        /* Determine signature size */
1114
        size_t ss;
26✔
1115
        if (EVP_DigestSign(mdctx, NULL, &ss, data, size) != 1)
26✔
1116
                return log_openssl_errors("Failed to determine size of signature");
×
1117

1118
        _cleanup_free_ void *sig = malloc(ss);
26✔
1119
        if (!sig)
26✔
1120
                return log_oom_debug();
×
1121

1122
        if (EVP_DigestSign(mdctx, sig, &ss, data, size) != 1)
26✔
1123
                return log_openssl_errors("Failed to sign data");
×
1124

1125
        *ret = TAKE_PTR(sig);
26✔
1126
        *ret_size = ss;
26✔
1127
        return 0;
26✔
1128
}
1129

1130
int pkcs7_new(X509 *certificate, EVP_PKEY *private_key, const char *hash_algorithm, PKCS7 **ret_p7, PKCS7_SIGNER_INFO **ret_si) {
16✔
1131
        assert(certificate);
16✔
1132
        assert(ret_p7);
16✔
1133

1134
        /* This function sets up a new PKCS7 signing context. If a private key is provided, the context is
1135
         * set up for "in-band" signing with PKCS7_dataFinal(). If a private key is not provided, the context
1136
         * is set up for "out-of-band" signing, meaning the signature has to be provided by the user and
1137
         * copied into the signer info's "enc_digest" field. If the signing hash algorithm is not provided,
1138
         * SHA-256 is used. */
1139

1140
        _cleanup_(PKCS7_freep) PKCS7 *p7 = PKCS7_new();
32✔
1141
        if (!p7)
16✔
1142
                return log_oom();
×
1143

1144
        if (PKCS7_set_type(p7, NID_pkcs7_signed) == 0)
16✔
1145
                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 type: %s",
×
1146
                                       ERR_error_string(ERR_get_error(), NULL));
1147

1148
        if (PKCS7_content_new(p7, NID_pkcs7_data) == 0)
16✔
1149
                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 content: %s",
×
1150
                                       ERR_error_string(ERR_get_error(), NULL));
1151

1152
        if (PKCS7_add_certificate(p7, certificate) == 0)
16✔
1153
                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 certificate: %s",
×
1154
                                       ERR_error_string(ERR_get_error(), NULL));
1155

1156
        int x509_pknid = 0;
16✔
1157
        if (X509_get_signature_info(certificate, NULL, &x509_pknid, NULL, NULL) == 0)
16✔
1158
                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get X509 digest NID: %s",
×
1159
                                       ERR_error_string(ERR_get_error(), NULL));
1160

1161
        const EVP_MD *md = EVP_get_digestbyname(hash_algorithm ?: "SHA256");
26✔
1162
        if (!md)
16✔
1163
                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get digest algorithm '%s'",
×
1164
                                       hash_algorithm ?: "SHA256");
1165

1166
        _cleanup_(PKCS7_SIGNER_INFO_freep) PKCS7_SIGNER_INFO *si = PKCS7_SIGNER_INFO_new();
32✔
1167
        if (!si)
16✔
1168
                return log_oom();
×
1169

1170
        if (private_key) {
16✔
1171
                if (PKCS7_SIGNER_INFO_set(si, certificate, private_key, md) <= 0)
2✔
1172
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure signer info: %s",
×
1173
                                               ERR_error_string(ERR_get_error(), NULL));
1174
        } else {
1175
                if (ASN1_INTEGER_set(si->version, 1) == 0)
14✔
1176
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info version: %s",
×
1177
                                               ERR_error_string(ERR_get_error(), NULL));
1178

1179
                if (X509_NAME_set(&si->issuer_and_serial->issuer, X509_get_issuer_name(certificate)) == 0)
14✔
1180
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info issuer: %s",
×
1181
                                               ERR_error_string(ERR_get_error(), NULL));
1182

1183
                ASN1_INTEGER_free(si->issuer_and_serial->serial);
14✔
1184
                si->issuer_and_serial->serial = ASN1_INTEGER_dup(X509_get0_serialNumber(certificate));
14✔
1185
                if (!si->issuer_and_serial->serial)
14✔
1186
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info serial: %s",
×
1187
                                               ERR_error_string(ERR_get_error(), NULL));
1188

1189
                if (X509_ALGOR_set0(si->digest_alg, OBJ_nid2obj(EVP_MD_type(md)), V_ASN1_NULL, NULL) == 0)
14✔
1190
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info digest algorithm: %s",
×
1191
                                               ERR_error_string(ERR_get_error(), NULL));
1192

1193
                if (X509_ALGOR_set0(si->digest_enc_alg, OBJ_nid2obj(x509_pknid), V_ASN1_NULL, NULL) == 0)
14✔
1194
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info signing algorithm: %s",
×
1195
                                               ERR_error_string(ERR_get_error(), NULL));
1196
        }
1197

1198
        if (PKCS7_add_signer(p7, si) == 0)
16✔
1199
                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 signer info: %s",
×
1200
                                       ERR_error_string(ERR_get_error(), NULL));
1201

1202
        *ret_p7 = TAKE_PTR(p7);
16✔
1203
        if (ret_si)
16✔
1204
                /* We do not pass ownership here, 'si' object remains owned by 'p7' object. */
1205
                *ret_si = si;
16✔
1206

1207
        TAKE_PTR(si);
1208

1209
        return 0;
1210
}
1211

1212
int string_hashsum(
5✔
1213
                const char *s,
1214
                size_t len,
1215
                const char *md_algorithm,
1216
                char **ret) {
1217

1218
        _cleanup_free_ void *hash = NULL;
10✔
1219
        size_t hash_size;
5✔
1220
        _cleanup_free_ char *enc = NULL;
5✔
1221
        int r;
5✔
1222

1223
        assert(s || len == 0);
5✔
1224
        assert(md_algorithm);
5✔
1225
        assert(ret);
5✔
1226

1227
        r = openssl_digest(md_algorithm, s, len, &hash, &hash_size);
5✔
1228
        if (r < 0)
5✔
1229
                return r;
1230

1231
        enc = hexmem(hash, hash_size);
5✔
1232
        if (!enc)
5✔
1233
                return -ENOMEM;
1234

1235
        *ret = TAKE_PTR(enc);
5✔
1236
        return 0;
5✔
1237
}
1238

1239
static int ecc_pkey_generate_volume_keys(
3✔
1240
                EVP_PKEY *pkey,
1241
                void **ret_decrypted_key,
1242
                size_t *ret_decrypted_key_size,
1243
                void **ret_saved_key,
1244
                size_t *ret_saved_key_size) {
1245

1246
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_new = NULL;
3✔
1247
        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
3✔
1248
        _cleanup_free_ unsigned char *saved_key = NULL;
×
1249
        size_t decrypted_key_size, saved_key_size;
3✔
1250
        int nid = NID_undef;
3✔
1251
        int r;
3✔
1252

1253
#if OPENSSL_VERSION_MAJOR >= 3
1254
        _cleanup_free_ char *curve_name = NULL;
3✔
1255
        size_t len = 0;
3✔
1256

1257
        if (EVP_PKEY_get_group_name(pkey, NULL, 0, &len) != 1 || len == 0)
3✔
1258
                return log_openssl_errors("Failed to determine PKEY group name length");
×
1259

1260
        len++;
3✔
1261
        curve_name = new(char, len);
3✔
1262
        if (!curve_name)
3✔
1263
                return log_oom_debug();
×
1264

1265
        if (EVP_PKEY_get_group_name(pkey, curve_name, len, &len) != 1)
3✔
1266
                return log_openssl_errors("Failed to get PKEY group name");
×
1267

1268
        nid = OBJ_sn2nid(curve_name);
3✔
1269
#else
1270
        EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
1271
        if (!ec_key)
1272
                return log_openssl_errors("PKEY doesn't have EC_KEY associated");
1273

1274
        if (EC_KEY_check_key(ec_key) != 1)
1275
                return log_openssl_errors("EC_KEY associated with PKEY is not valid");
1276

1277
        nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
1278
#endif
1279

1280
        r = ecc_pkey_new(nid, &pkey_new);
3✔
1281
        if (r < 0)
3✔
1282
                return log_debug_errno(r, "Failed to generate a new EC keypair: %m");
×
1283

1284
        r = ecc_ecdh(pkey_new, pkey, &decrypted_key, &decrypted_key_size);
3✔
1285
        if (r < 0)
3✔
1286
                return log_debug_errno(r, "Failed to derive shared secret: %m");
×
1287

1288
#if OPENSSL_VERSION_MAJOR >= 3
1289
        /* EVP_PKEY_get1_encoded_public_key() always returns uncompressed format of EC points.
1290
           See https://github.com/openssl/openssl/discussions/22835 */
1291
        saved_key_size = EVP_PKEY_get1_encoded_public_key(pkey_new, &saved_key);
3✔
1292
        if (saved_key_size == 0)
3✔
1293
                return log_openssl_errors("Failed to convert the generated public key to SEC1 format");
×
1294
#else
1295
        EC_KEY *ec_key_new = EVP_PKEY_get0_EC_KEY(pkey_new);
1296
        if (!ec_key_new)
1297
                return log_openssl_errors("The generated key doesn't have associated EC_KEY");
1298

1299
        if (EC_KEY_check_key(ec_key_new) != 1)
1300
                return log_openssl_errors("EC_KEY associated with the generated key is not valid");
1301

1302
        saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new),
1303
                                            EC_KEY_get0_public_key(ec_key_new),
1304
                                            POINT_CONVERSION_UNCOMPRESSED,
1305
                                            NULL, 0, NULL);
1306
        if (saved_key_size == 0)
1307
                return log_openssl_errors("Failed to determine size of the generated public key");
1308

1309
        saved_key = malloc(saved_key_size);
1310
        if (!saved_key)
1311
                return log_oom_debug();
1312

1313
        saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new),
1314
                                            EC_KEY_get0_public_key(ec_key_new),
1315
                                            POINT_CONVERSION_UNCOMPRESSED,
1316
                                            saved_key, saved_key_size, NULL);
1317
        if (saved_key_size == 0)
1318
                return log_openssl_errors("Failed to convert the generated public key to SEC1 format");
1319
#endif
1320

1321
        *ret_decrypted_key = TAKE_PTR(decrypted_key);
3✔
1322
        *ret_decrypted_key_size = decrypted_key_size;
3✔
1323
        *ret_saved_key = TAKE_PTR(saved_key);
3✔
1324
        *ret_saved_key_size = saved_key_size;
3✔
1325
        return 0;
3✔
1326
}
1327

1328
static int rsa_pkey_generate_volume_keys(
3✔
1329
                EVP_PKEY *pkey,
1330
                void **ret_decrypted_key,
1331
                size_t *ret_decrypted_key_size,
1332
                void **ret_saved_key,
1333
                size_t *ret_saved_key_size) {
1334

1335
        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
3✔
1336
        _cleanup_free_ void *saved_key = NULL;
3✔
1337
        size_t decrypted_key_size, saved_key_size;
3✔
1338
        int r;
3✔
1339

1340
        r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size);
3✔
1341
        if (r < 0)
3✔
1342
                return log_debug_errno(r, "Failed to determine RSA public key size.");
×
1343

1344
        log_debug("Generating %zu bytes random key.", decrypted_key_size);
3✔
1345

1346
        decrypted_key = malloc(decrypted_key_size);
3✔
1347
        if (!decrypted_key)
3✔
1348
                return log_oom_debug();
×
1349

1350
        r = crypto_random_bytes(decrypted_key, decrypted_key_size);
3✔
1351
        if (r < 0)
3✔
1352
                return log_debug_errno(r, "Failed to generate random key: %m");
×
1353

1354
        r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &saved_key, &saved_key_size);
3✔
1355
        if (r < 0)
3✔
1356
                return log_debug_errno(r, "Failed to encrypt random key: %m");
×
1357

1358
        *ret_decrypted_key = TAKE_PTR(decrypted_key);
3✔
1359
        *ret_decrypted_key_size = decrypted_key_size;
3✔
1360
        *ret_saved_key = TAKE_PTR(saved_key);
3✔
1361
        *ret_saved_key_size = saved_key_size;
3✔
1362
        return 0;
3✔
1363
}
1364

1365
int pkey_generate_volume_keys(
6✔
1366
                EVP_PKEY *pkey,
1367
                void **ret_decrypted_key,
1368
                size_t *ret_decrypted_key_size,
1369
                void **ret_saved_key,
1370
                size_t *ret_saved_key_size) {
1371

1372
        assert(pkey);
6✔
1373
        assert(ret_decrypted_key);
6✔
1374
        assert(ret_decrypted_key_size);
6✔
1375
        assert(ret_saved_key);
6✔
1376
        assert(ret_saved_key_size);
6✔
1377

1378
#if OPENSSL_VERSION_MAJOR >= 3
1379
        int type = EVP_PKEY_get_base_id(pkey);
6✔
1380
#else
1381
        int type = EVP_PKEY_base_id(pkey);
1382
#endif
1383
        switch (type) {
6✔
1384

1385
        case EVP_PKEY_RSA:
3✔
1386
                return rsa_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
3✔
1387

1388
        case EVP_PKEY_EC:
3✔
1389
                return ecc_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size);
3✔
1390

1391
        case NID_undef:
1392
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine a type of public key.");
×
1393

1394
        default:
1395
                return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported public key type: %s", OBJ_nid2sn(type));
×
1396
        }
1397
}
1398

1399
static int load_key_from_provider(
×
1400
                const char *provider,
1401
                const char *private_key_uri,
1402
                EVP_PKEY **ret) {
1403

1404
        assert(provider);
×
1405
        assert(private_key_uri);
×
1406
        assert(ret);
×
1407

1408
#if OPENSSL_VERSION_MAJOR >= 3
1409
        /* Load the provider so that this can work without any custom written configuration in /etc/.
1410
         * Also load the 'default' as that seems to be the recommendation. */
1411
        if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true))
×
1412
                return log_openssl_errors("Failed to load OpenSSL provider '%s'", provider);
×
1413
        if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true))
×
1414
                return log_openssl_errors("Failed to load OpenSSL provider 'default'");
×
1415

1416
        _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open(
×
1417
                        private_key_uri,
1418
                        /*ui_method=*/ NULL,
1419
                        /*ui_method=*/ NULL,
1420
                        /* post_process= */ NULL,
1421
                        /* post_process_data= */ NULL);
1422
        if (!store)
×
1423
                return log_openssl_errors("Failed to open OpenSSL store via '%s'", private_key_uri);
×
1424

1425
        if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) == 0)
×
1426
                return log_openssl_errors("Failed to filter store by private keys");
×
1427

1428
        _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store);
×
1429
        if (!info)
×
1430
                return log_openssl_errors("Failed to load OpenSSL store via '%s'", private_key_uri);
×
1431

1432
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = OSSL_STORE_INFO_get1_PKEY(info);
×
1433
        if (!private_key)
×
1434
                return log_openssl_errors("Failed to load private key via '%s'", private_key_uri);
×
1435

1436
        *ret = TAKE_PTR(private_key);
×
1437

1438
        return 0;
×
1439
#else
1440
        return -EOPNOTSUPP;
1441
#endif
1442
}
1443

1444
static int load_key_from_engine(const char *engine, const char *private_key_uri, EVP_PKEY **ret) {
×
1445
        assert(engine);
×
1446
        assert(private_key_uri);
×
1447
        assert(ret);
×
1448

1449
#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
1450
        DISABLE_WARNING_DEPRECATED_DECLARATIONS;
×
1451
        _cleanup_(ENGINE_freep) ENGINE *e = ENGINE_by_id(engine);
×
1452
        if (!e)
×
1453
                return log_openssl_errors("Failed to load signing engine '%s'", engine);
×
1454

1455
        if (ENGINE_init(e) == 0)
×
1456
                return log_openssl_errors("Failed to initialize signing engine '%s'", engine);
×
1457

1458
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = ENGINE_load_private_key(e, private_key_uri, /*ui_method=*/ NULL, /*callback_data=*/ NULL);
×
1459
        if (!private_key)
×
1460
                return log_openssl_errors("Failed to load private key from '%s'", private_key_uri);
×
1461
        REENABLE_WARNING;
×
1462

1463
        *ret = TAKE_PTR(private_key);
×
1464

1465
        return 0;
×
1466
#else
1467
        return -EOPNOTSUPP;
1468
#endif
1469
}
1470

1471
#ifndef OPENSSL_NO_UI_CONSOLE
1472
static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) {
×
1473
        int r;
×
1474

1475
        switch(UI_get_string_type(uis)) {
×
1476
        case UIT_PROMPT: {
×
1477
                /* If no ask password request was configured use the default openssl UI. */
1478
                AskPasswordRequest *req = (AskPasswordRequest*) UI_method_get_ex_data(UI_get_method(ui), 0);
×
1479
                if (!req)
×
1480
                        return (UI_method_get_reader(UI_OpenSSL()))(ui, uis);
×
1481

1482
                req->message = UI_get0_output_string(uis);
×
1483

1484
                _cleanup_strv_free_ char **l = NULL;
×
1485
                r = ask_password_auto(req, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &l);
×
1486
                if (r < 0) {
×
1487
                        log_error_errno(r, "Failed to query for PIN: %m");
×
1488
                        return 0;
×
1489
                }
1490

1491
                if (strv_length(l) != 1) {
×
1492
                        log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected only a single password/pin.");
×
1493
                        return 0;
×
1494
                }
1495

1496
                if (UI_set_result(ui, uis, *l) != 0) {
×
1497
                        log_openssl_errors("Failed to set user interface result");
×
1498
                        return 0;
×
1499
                }
1500

1501
                return 1;
1502
        }
1503
        default:
×
1504
                return (UI_method_get_reader(UI_OpenSSL()))(ui, uis);
×
1505
        }
1506
}
1507
#endif
1508

1509
static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) {
16✔
1510
        _cleanup_(erase_and_freep) char *rawkey = NULL;
16✔
1511
        _cleanup_(BIO_freep) BIO *kb = NULL;
16✔
1512
        _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL;
16✔
1513
        size_t rawkeysz;
16✔
1514
        int r;
16✔
1515

1516
        assert(path);
16✔
1517
        assert(ret);
16✔
1518

1519
        r = read_full_file_full(
16✔
1520
                        AT_FDCWD, path, UINT64_MAX, SIZE_MAX,
1521
                        READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
1522
                        NULL,
1523
                        &rawkey, &rawkeysz);
1524
        if (r < 0)
16✔
1525
                return log_debug_errno(r, "Failed to read key file '%s': %m", path);
×
1526

1527
        kb = BIO_new_mem_buf(rawkey, rawkeysz);
16✔
1528
        if (!kb)
16✔
1529
                return log_oom_debug();
×
1530

1531
        pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL);
16✔
1532
        if (!pk)
16✔
1533
                return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s",
×
1534
                                       ERR_error_string(ERR_get_error(), NULL));
1535

1536
        if (ret)
16✔
1537
                *ret = TAKE_PTR(pk);
16✔
1538

1539
        return 0;
16✔
1540
}
1541

1542
static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSSLAskPasswordUI **ret) {
×
1543
        assert(ret);
×
1544

1545
#ifndef OPENSSL_NO_UI_CONSOLE
1546
        _cleanup_(UI_destroy_methodp) UI_METHOD *method = UI_create_method("systemd-ask-password");
×
1547
        if (!method)
×
1548
                return log_openssl_errors("Failed to initialize openssl user interface");
×
1549

1550
        if (UI_method_set_reader(method, openssl_ask_password_ui_read) != 0)
×
1551
                return log_openssl_errors("Failed to set openssl user interface reader");
×
1552

1553
        OpenSSLAskPasswordUI *ui = new(OpenSSLAskPasswordUI, 1);
×
1554
        if (!ui)
×
1555
                return log_oom_debug();
×
1556

1557
        *ui = (OpenSSLAskPasswordUI) {
×
1558
                .method = TAKE_PTR(method),
×
1559
                .request = *request,
×
1560
        };
1561

1562
        UI_set_default_method(ui->method);
×
1563

1564
        if (UI_method_set_ex_data(ui->method, 0, &ui->request) == 0)
×
1565
                return log_openssl_errors("Failed to set extra data for UI method");
×
1566

1567
        *ret = TAKE_PTR(ui);
×
1568
        return 0;
×
1569
#else
1570
        return -EOPNOTSUPP;
1571
#endif
1572
}
1573

1574
static int load_x509_certificate_from_file(const char *path, X509 **ret) {
30✔
1575
        _cleanup_free_ char *rawcert = NULL;
30✔
1576
        _cleanup_(X509_freep) X509 *cert = NULL;
30✔
1577
        _cleanup_(BIO_freep) BIO *cb = NULL;
30✔
1578
        size_t rawcertsz;
30✔
1579
        int r;
30✔
1580

1581
        assert(path);
30✔
1582
        assert(ret);
30✔
1583

1584
        r = read_full_file_full(
30✔
1585
                        AT_FDCWD, path, UINT64_MAX, SIZE_MAX,
1586
                        READ_FULL_FILE_CONNECT_SOCKET,
1587
                        NULL,
1588
                        &rawcert, &rawcertsz);
1589
        if (r < 0)
30✔
1590
                return log_debug_errno(r, "Failed to read certificate file '%s': %m", path);
×
1591

1592
        cb = BIO_new_mem_buf(rawcert, rawcertsz);
30✔
1593
        if (!cb)
30✔
1594
                return log_oom_debug();
×
1595

1596
        cert = PEM_read_bio_X509(cb, NULL, NULL, NULL);
30✔
1597
        if (!cert)
30✔
1598
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s",
×
1599
                                       ERR_error_string(ERR_get_error(), NULL));
1600

1601
        if (ret)
30✔
1602
                *ret = TAKE_PTR(cert);
30✔
1603

1604
        return 0;
30✔
1605
}
1606

1607
static int load_x509_certificate_from_provider(const char *provider, const char *certificate_uri, X509 **ret) {
×
1608
        assert(provider);
×
1609
        assert(certificate_uri);
×
1610
        assert(ret);
×
1611

1612
#if OPENSSL_VERSION_MAJOR >= 3
1613
        /* Load the provider so that this can work without any custom written configuration in /etc/.
1614
         * Also load the 'default' as that seems to be the recommendation. */
1615
        if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true))
×
1616
                return log_openssl_errors("Failed to load OpenSSL provider '%s'", provider);
×
1617
        if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true))
×
1618
                return log_openssl_errors("Failed to load OpenSSL provider 'default'");
×
1619

1620
        _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open(
×
1621
                        certificate_uri,
1622
                        /*ui_method=*/ NULL,
1623
                        /*ui_method=*/ NULL,
1624
                        /* post_process= */ NULL,
1625
                        /* post_process_data= */ NULL);
1626
        if (!store)
×
1627
                return log_openssl_errors("Failed to open OpenSSL store via '%s'", certificate_uri);
×
1628

1629
        if (OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) == 0)
×
1630
                return log_openssl_errors("Failed to filter store by X.509 certificates");
×
1631

1632
        _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store);
×
1633
        if (!info)
×
1634
                return log_openssl_errors("Failed to load OpenSSL store via '%s'", certificate_uri);
×
1635

1636
        _cleanup_(X509_freep) X509 *cert = OSSL_STORE_INFO_get1_CERT(info);
×
1637
        if (!cert)
×
1638
                return log_openssl_errors("Failed to load certificate via '%s'", certificate_uri);
×
1639

1640
        *ret = TAKE_PTR(cert);
×
1641

1642
        return 0;
×
1643
#else
1644
        return -EOPNOTSUPP;
1645
#endif
1646
}
1647

1648
OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) {
×
1649
        if (!ui)
×
1650
                return NULL;
1651

1652
#ifndef OPENSSL_NO_UI_CONSOLE
1653
        assert(UI_get_default_method() == ui->method);
×
1654
        UI_set_default_method(UI_OpenSSL());
×
1655
        UI_destroy_method(ui->method);
×
1656
#endif
1657
        return mfree(ui);
×
1658
}
1659

1660
int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) {
16✔
1661
        _cleanup_free_ uint8_t *der = NULL;
16✔
1662
        int dersz;
16✔
1663

1664
        assert(cert);
16✔
1665

1666
        dersz = i2d_X509(cert, &der);
16✔
1667
        if (dersz < 0)
16✔
1668
                return log_openssl_errors("Unable to convert PEM certificate to DER format");
×
1669

1670
        sha256_direct(der, dersz, buffer);
16✔
1671
        return 0;
1672
}
1673

1674
int openssl_load_x509_certificate(
30✔
1675
                CertificateSourceType certificate_source_type,
1676
                const char *certificate_source,
1677
                const char *certificate,
1678
                X509 **ret) {
1679

1680
        int r;
30✔
1681

1682
        assert(certificate);
30✔
1683

1684
        switch (certificate_source_type) {
30✔
1685

1686
        case OPENSSL_CERTIFICATE_SOURCE_FILE:
30✔
1687
                r = load_x509_certificate_from_file(certificate, ret);
30✔
1688
                break;
30✔
1689
        case OPENSSL_CERTIFICATE_SOURCE_PROVIDER:
×
1690
                r = load_x509_certificate_from_provider(certificate_source, certificate, ret);
×
1691
                break;
×
1692
        default:
×
1693
                assert_not_reached();
×
1694
        }
1695
        if (r < 0)
30✔
1696
                return log_debug_errno(
×
1697
                                r,
1698
                                "Failed to load certificate '%s' from OpenSSL certificate source %s: %m",
1699
                                certificate,
1700
                                certificate_source);
1701

1702
        return 0;
1703
}
1704

1705
int openssl_load_private_key(
16✔
1706
                KeySourceType private_key_source_type,
1707
                const char *private_key_source,
1708
                const char *private_key,
1709
                const AskPasswordRequest *request,
1710
                EVP_PKEY **ret_private_key,
1711
                OpenSSLAskPasswordUI **ret_user_interface) {
1712

1713
        int r;
16✔
1714

1715
        assert(private_key);
16✔
1716
        assert(request);
16✔
1717
        assert(ret_private_key);
16✔
1718

1719
        if (private_key_source_type == OPENSSL_KEY_SOURCE_FILE) {
16✔
1720
                r = openssl_load_private_key_from_file(private_key, ret_private_key);
16✔
1721
                if (r < 0)
16✔
1722
                        return r;
1723

1724
                if (ret_user_interface)
16✔
1725
                        *ret_user_interface = NULL;
6✔
1726
        } else {
1727
                _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
×
1728
                r = openssl_ask_password_ui_new(request, &ui);
×
1729
                if (r < 0)
×
1730
                        return log_debug_errno(r, "Failed to allocate ask-password user interface: %m");
×
1731

1732
                switch (private_key_source_type) {
×
1733

1734
                case OPENSSL_KEY_SOURCE_ENGINE:
×
1735
                        r = load_key_from_engine(private_key_source, private_key, ret_private_key);
×
1736
                        break;
1737
                case OPENSSL_KEY_SOURCE_PROVIDER:
×
1738
                        r = load_key_from_provider(private_key_source, private_key, ret_private_key);
×
1739
                        break;
1740
                default:
×
1741
                        assert_not_reached();
×
1742
                }
1743
                if (r < 0)
×
1744
                        return log_debug_errno(
×
1745
                                        r,
1746
                                        "Failed to load key '%s' from OpenSSL private key source %s: %m",
1747
                                        private_key,
1748
                                        private_key_source);
1749

1750
                if (ret_user_interface)
×
1751
                        *ret_user_interface = TAKE_PTR(ui);
×
1752
        }
1753

1754
        return 0;
1755
}
1756
#endif
1757

1758
int parse_openssl_certificate_source_argument(
×
1759
                const char *argument,
1760
                char **certificate_source,
1761
                CertificateSourceType *certificate_source_type) {
1762

1763
        CertificateSourceType type;
×
1764
        const char *e = NULL;
×
1765
        int r;
×
1766

1767
        assert(argument);
×
1768
        assert(certificate_source);
×
1769
        assert(certificate_source_type);
×
1770

1771
        if (streq(argument, "file"))
×
1772
                type = OPENSSL_CERTIFICATE_SOURCE_FILE;
1773
        else if ((e = startswith(argument, "provider:")))
×
1774
                type = OPENSSL_CERTIFICATE_SOURCE_PROVIDER;
1775
        else
1776
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid certificate source '%s'", argument);
×
1777

1778
        r = free_and_strdup_warn(certificate_source, e);
×
1779
        if (r < 0)
×
1780
                return r;
1781

1782
        *certificate_source_type = type;
×
1783

1784
        return 0;
×
1785
}
1786

1787
int parse_openssl_key_source_argument(
×
1788
                const char *argument,
1789
                char **private_key_source,
1790
                KeySourceType *private_key_source_type) {
1791

1792
        KeySourceType type;
×
1793
        const char *e = NULL;
×
1794
        int r;
×
1795

1796
        assert(argument);
×
1797
        assert(private_key_source);
×
1798
        assert(private_key_source_type);
×
1799

1800
        if (streq(argument, "file"))
×
1801
                type = OPENSSL_KEY_SOURCE_FILE;
1802
        else if ((e = startswith(argument, "engine:")))
×
1803
                type = OPENSSL_KEY_SOURCE_ENGINE;
1804
        else if ((e = startswith(argument, "provider:")))
×
1805
                type = OPENSSL_KEY_SOURCE_PROVIDER;
1806
        else
1807
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid private key source '%s'", argument);
×
1808

1809
        r = free_and_strdup_warn(private_key_source, e);
×
1810
        if (r < 0)
×
1811
                return r;
1812

1813
        *private_key_source_type = type;
×
1814

1815
        return 0;
×
1816
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc