]>
Commit | Line | Data |
---|---|---|
30e31503 ZJS |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
f2d5df8a | 3 | #include "alloc-util.h" |
eac5336c | 4 | #include "ask-password-api.h" |
a65a25be | 5 | #include "fd-util.h" |
eac5336c | 6 | #include "fileio.h" |
7e8facb3 | 7 | #include "hexdecoct.h" |
93a1f792 | 8 | #include "log.h" |
85686b37 | 9 | #include "memory-util.h" |
6a2097dd | 10 | #include "memstream-util.h" |
a65a25be | 11 | #include "openssl-util.h" |
876206f2 | 12 | #include "random-util.h" |
a65a25be | 13 | #include "string-util.h" |
eac5336c | 14 | #include "strv.h" |
f2d5df8a LP |
15 | |
16 | #if HAVE_OPENSSL | |
81d61d6a | 17 | # include <openssl/ec.h> |
1cf40697 | 18 | # include <openssl/rsa.h> |
81d61d6a NL |
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 | ||
d1d16c62 | 27 | #ifndef OPENSSL_NO_UI_CONSOLE |
0bf70b19 | 28 | DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UI_METHOD*, UI_destroy_method, NULL); |
d1d16c62 | 29 | #endif |
0bf70b19 | 30 | |
3d05c058 VS |
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 | |
60696b22 DS |
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__) \ | |
968d232d | 42 | ?: log_debug_errno(SYNTHETIC_ERRNO(EIO), fmt ": No OpenSSL errors.", ##__VA_ARGS__); \ |
60696b22 DS |
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 | ||
6a2097dd | 57 | int openssl_pubkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret) { |
4af788c7 DS |
58 | assert(pem); |
59 | assert(ret); | |
60 | ||
6a2097dd LP |
61 | if (pem_size == SIZE_MAX) |
62 | pem_size = strlen(pem); | |
63 | ||
4af788c7 DS |
64 | _cleanup_fclose_ FILE *f = NULL; |
65 | f = fmemopen((void*) pem, pem_size, "r"); | |
66 | if (!f) | |
67 | return log_oom_debug(); | |
68 | ||
6a2097dd | 69 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = PEM_read_PUBKEY(f, /* x= */ NULL, /* pam_password_cb= */ NULL, /* userdata= */ NULL); |
4af788c7 | 70 | if (!pkey) |
60696b22 | 71 | return log_openssl_errors("Failed to parse PEM"); |
4af788c7 DS |
72 | |
73 | *ret = TAKE_PTR(pkey); | |
4af788c7 DS |
74 | return 0; |
75 | } | |
76 | ||
6a2097dd LP |
77 | int openssl_pubkey_to_pem(EVP_PKEY *pkey, char **ret) { |
78 | assert(pkey); | |
79 | assert(ret); | |
80 | ||
81 | _cleanup_(memstream_done) MemStream m = {}; | |
82 | FILE *f = memstream_init(&m); | |
83 | if (!f) | |
84 | return -ENOMEM; | |
85 | ||
86 | if (PEM_write_PUBKEY(f, pkey) <= 0) | |
87 | return -EIO; | |
88 | ||
89 | return memstream_finalize(&m, ret, /* ret_size= */ NULL); | |
90 | } | |
91 | ||
c52a003d DS |
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) { | |
97 | assert(digest_alg); | |
98 | assert(ret_digest_size); | |
99 | ||
100 | #if OPENSSL_VERSION_MAJOR >= 3 | |
101 | _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL); | |
102 | #else | |
103 | const EVP_MD *md = EVP_get_digestbyname(digest_alg); | |
104 | #endif | |
105 | if (!md) | |
106 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
107 | "Digest algorithm '%s' not supported.", digest_alg); | |
108 | ||
109 | size_t digest_size; | |
110 | #if OPENSSL_VERSION_MAJOR >= 3 | |
111 | digest_size = EVP_MD_get_size(md); | |
112 | #else | |
113 | digest_size = EVP_MD_size(md); | |
114 | #endif | |
115 | if (digest_size == 0) | |
116 | return log_openssl_errors("Failed to get Digest size"); | |
117 | ||
118 | *ret_digest_size = digest_size; | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
bed4831c DS |
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( | |
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; | |
133 | ||
134 | assert(digest_alg); | |
135 | assert(data || n_data == 0); | |
136 | assert(ret_digest); | |
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); | |
141 | #else | |
142 | const EVP_MD *md = EVP_get_digestbyname(digest_alg); | |
143 | #endif | |
144 | if (!md) | |
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(); | |
149 | if (!ctx) | |
150 | return log_openssl_errors("Failed to create new EVP_MD_CTX"); | |
151 | ||
152 | if (!EVP_DigestInit_ex(ctx, md, NULL)) | |
fcdd21ec | 153 | return log_openssl_errors("Failed to initialize EVP_MD_CTX"); |
bed4831c DS |
154 | |
155 | for (size_t i = 0; i < n_data; i++) | |
156 | if (!EVP_DigestUpdate(ctx, data[i].iov_base, data[i].iov_len)) | |
157 | return log_openssl_errors("Failed to update Digest"); | |
158 | ||
159 | size_t digest_size; | |
160 | r = openssl_digest_size(digest_alg, &digest_size); | |
161 | if (r < 0) | |
162 | return r; | |
163 | ||
164 | _cleanup_free_ void *buf = malloc(digest_size); | |
165 | if (!buf) | |
166 | return log_oom_debug(); | |
167 | ||
9ec7d7ae | 168 | unsigned size; |
bed4831c DS |
169 | if (!EVP_DigestFinal_ex(ctx, buf, &size)) |
170 | return log_openssl_errors("Failed to finalize Digest"); | |
171 | ||
172 | assert(size == digest_size); | |
173 | ||
174 | *ret_digest = TAKE_PTR(buf); | |
175 | if (ret_digest_size) | |
176 | *ret_digest_size = size; | |
177 | ||
178 | return 0; | |
179 | } | |
180 | ||
a95e8fa2 DS |
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( | |
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); | |
194 | assert(key); | |
195 | assert(data || n_data == 0); | |
196 | assert(ret_digest); | |
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); | |
201 | #else | |
202 | const EVP_MD *md = EVP_get_digestbyname(digest_alg); | |
203 | #endif | |
204 | if (!md) | |
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); | |
210 | if (!mac) | |
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); | |
214 | if (!ctx) | |
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(); | |
218 | if (!bld) | |
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)) | |
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); | |
225 | if (!params) | |
226 | return log_openssl_errors("Failed to build HMAC OSSL_PARAM"); | |
227 | ||
228 | if (!EVP_MAC_init(ctx, key, key_size, params)) | |
fcdd21ec | 229 | return log_openssl_errors("Failed to initialize EVP_MAC_CTX"); |
a95e8fa2 DS |
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++) | |
240 | #if OPENSSL_VERSION_MAJOR >= 3 | |
241 | if (!EVP_MAC_update(ctx, data[i].iov_base, data[i].iov_len)) | |
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; | |
248 | #if OPENSSL_VERSION_MAJOR >= 3 | |
249 | digest_size = EVP_MAC_CTX_get_mac_size(ctx); | |
250 | #else | |
251 | digest_size = HMAC_size(ctx); | |
252 | #endif | |
253 | if (digest_size == 0) | |
254 | return log_openssl_errors("Failed to get HMAC digest size"); | |
255 | ||
256 | _cleanup_free_ void *buf = malloc(digest_size); | |
257 | if (!buf) | |
258 | return log_oom_debug(); | |
259 | ||
260 | #if OPENSSL_VERSION_MAJOR >= 3 | |
261 | size_t size; | |
262 | if (!EVP_MAC_final(ctx, buf, &size, digest_size)) | |
263 | #else | |
9ec7d7ae | 264 | unsigned size; |
a95e8fa2 DS |
265 | if (!HMAC_Final(ctx, buf, &size)) |
266 | #endif | |
267 | return log_openssl_errors("Failed to finalize HMAC"); | |
268 | ||
269 | assert(size == digest_size); | |
270 | ||
271 | *ret_digest = TAKE_PTR(buf); | |
272 | if (ret_digest_size) | |
273 | *ret_digest_size = size; | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
58f215a0 DS |
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( | |
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); | |
297 | assert(bits > 0); | |
298 | assert(mode); | |
299 | assert(key); | |
300 | assert(iv || iv_size == 0); | |
301 | assert(data || n_data == 0); | |
302 | assert(ret); | |
303 | assert(ret_size); | |
304 | ||
305 | _cleanup_free_ char *cipher_alg = NULL; | |
306 | if (asprintf(&cipher_alg, "%s-%zu-%s", alg, bits, mode) < 0) | |
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); | |
311 | #else | |
312 | const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_alg); | |
313 | #endif | |
314 | if (!cipher) | |
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(); | |
319 | if (!ctx) | |
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); | |
324 | assert(cipher_key_length >= 0); | |
325 | if ((size_t) cipher_key_length > key_size) | |
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); | |
331 | assert(cipher_iv_length >= 0); | |
332 | _cleanup_free_ void *zero_iv = NULL; | |
333 | if (iv_size == 0) { | |
334 | zero_iv = malloc0(cipher_iv_length); | |
335 | if (!zero_iv) | |
336 | return log_oom_debug(); | |
337 | ||
338 | iv = zero_iv; | |
339 | iv_size = (size_t) cipher_iv_length; | |
340 | } | |
341 | if ((size_t) cipher_iv_length > iv_size) | |
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)) | |
346 | return log_openssl_errors("Failed to initialize EVP_CIPHER_CTX."); | |
347 | ||
348 | int cipher_block_size = EVP_CIPHER_CTX_block_size(ctx); | |
349 | assert(cipher_block_size > 0); | |
350 | ||
351 | _cleanup_free_ uint8_t *buf = NULL; | |
352 | size_t size = 0; | |
353 | ||
354 | for (size_t i = 0; i < n_data; i++) { | |
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)) | |
357 | return log_oom_debug(); | |
358 | ||
359 | int update_size; | |
360 | if (!EVP_EncryptUpdate(ctx, &buf[size], &update_size, data[i].iov_base, data[i].iov_len)) | |
361 | return log_openssl_errors("Failed to update Cipher."); | |
362 | ||
363 | size += update_size; | |
364 | } | |
365 | ||
366 | if (!GREEDY_REALLOC(buf, size + cipher_block_size)) | |
367 | return log_oom_debug(); | |
368 | ||
369 | int final_size; | |
370 | if (!EVP_EncryptFinal_ex(ctx, &buf[size], &final_size)) | |
371 | return log_openssl_errors("Failed to finalize Cipher."); | |
372 | ||
373 | *ret = TAKE_PTR(buf); | |
374 | *ret_size = size + final_size; | |
375 | ||
376 | return 0; | |
377 | } | |
378 | ||
8c2205bb DS |
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( | |
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); | |
396 | assert(key); | |
397 | assert(derive_size > 0); | |
398 | assert(ret); | |
399 | ||
400 | _cleanup_(EVP_KDF_freep) EVP_KDF *kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL); | |
401 | if (!kdf) | |
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); | |
405 | if (!ctx) | |
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(); | |
409 | if (!bld) | |
410 | return log_openssl_errors("Failed to create new OSSL_PARAM_BLD"); | |
411 | ||
412 | _cleanup_free_ void *buf = malloc(derive_size); | |
413 | if (!buf) | |
414 | return log_oom_debug(); | |
415 | ||
416 | if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_DIGEST, (char*) digest, 0)) | |
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)) | |
420 | return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_KEY"); | |
421 | ||
422 | if (salt) | |
423 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SALT, (char*) salt, salt_size)) | |
424 | return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_SALT"); | |
425 | ||
426 | if (info) | |
427 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_INFO, (char*) info, info_size)) | |
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); | |
431 | if (!params) | |
432 | return log_openssl_errors("Failed to build KDF-SS OSSL_PARAM"); | |
433 | ||
434 | if (EVP_KDF_derive(ctx, buf, derive_size, params) <= 0) | |
968d232d | 435 | return log_openssl_errors("OpenSSL KDF-SS derive failed"); |
8c2205bb DS |
436 | |
437 | *ret = TAKE_PTR(buf); | |
438 | ||
439 | return 0; | |
440 | #else | |
968d232d | 441 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "KDF-SS requires OpenSSL >= 3."); |
8c2205bb DS |
442 | #endif |
443 | } | |
444 | ||
a65a25be | 445 | /* Perform Key-Based HMAC KDF. The mode must be "COUNTER" or "FEEDBACK". The parameter naming is from the |
968d232d | 446 | * OpenSSL api, and maps to SP800-108 naming as "...key, salt, info, and seed correspond to KI, Label, |
a65a25be DS |
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( | |
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); | |
466 | assert(strcaseeq(mode, "COUNTER") || strcaseeq(mode, "FEEDBACK")); | |
467 | assert(digest); | |
468 | assert(key || key_size == 0); | |
469 | assert(salt || salt_size == 0); | |
470 | assert(info || info_size == 0); | |
471 | assert(seed || seed_size == 0); | |
472 | assert(derive_size > 0); | |
473 | assert(ret); | |
474 | ||
475 | _cleanup_(EVP_KDF_freep) EVP_KDF *kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL); | |
476 | if (!kdf) | |
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); | |
480 | if (!ctx) | |
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(); | |
484 | if (!bld) | |
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)) | |
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)) | |
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)) | |
494 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_DIGEST"); | |
495 | ||
496 | if (key) | |
497 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_KEY, (char*) key, key_size)) | |
498 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_KEY"); | |
499 | ||
500 | if (salt) | |
501 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SALT, (char*) salt, salt_size)) | |
502 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_SALT"); | |
503 | ||
504 | if (info) | |
505 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_INFO, (char*) info, info_size)) | |
506 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_INFO"); | |
507 | ||
508 | if (seed) | |
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); | |
513 | if (!params) | |
514 | return log_openssl_errors("Failed to build KDF-KB OSSL_PARAM"); | |
515 | ||
516 | _cleanup_free_ void *buf = malloc(derive_size); | |
517 | if (!buf) | |
518 | return log_oom_debug(); | |
519 | ||
520 | if (EVP_KDF_derive(ctx, buf, derive_size, params) <= 0) | |
968d232d | 521 | return log_openssl_errors("OpenSSL KDF-KB derive failed"); |
a65a25be DS |
522 | |
523 | *ret = TAKE_PTR(buf); | |
524 | ||
525 | return 0; | |
526 | #else | |
968d232d | 527 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "KDF-KB requires OpenSSL >= 3."); |
a65a25be DS |
528 | #endif |
529 | } | |
530 | ||
f2d5df8a LP |
531 | int rsa_encrypt_bytes( |
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; | |
539 | _cleanup_free_ void *b = NULL; | |
540 | size_t l; | |
541 | ||
542 | ctx = EVP_PKEY_CTX_new(pkey, NULL); | |
543 | if (!ctx) | |
60696b22 | 544 | return log_openssl_errors("Failed to allocate public key context"); |
f2d5df8a LP |
545 | |
546 | if (EVP_PKEY_encrypt_init(ctx) <= 0) | |
60696b22 | 547 | return log_openssl_errors("Failed to initialize public key context"); |
f2d5df8a LP |
548 | |
549 | if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) | |
60696b22 | 550 | return log_openssl_errors("Failed to configure PKCS#1 padding"); |
f2d5df8a LP |
551 | |
552 | if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0) | |
60696b22 | 553 | return log_openssl_errors("Failed to determine encrypted key size"); |
f2d5df8a LP |
554 | |
555 | b = malloc(l); | |
556 | if (!b) | |
557 | return -ENOMEM; | |
558 | ||
559 | if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0) | |
60696b22 | 560 | return log_openssl_errors("Failed to determine encrypted key size"); |
f2d5df8a LP |
561 | |
562 | *ret_encrypt_key = TAKE_PTR(b); | |
563 | *ret_encrypt_key_size = l; | |
f2d5df8a LP |
564 | return 0; |
565 | } | |
d041e4fc | 566 | |
816b1dc4 DS |
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 | ||
d041e4fc LP |
634 | int rsa_pkey_to_suitable_key_size( |
635 | EVP_PKEY *pkey, | |
636 | size_t *ret_suitable_key_size) { | |
637 | ||
638 | size_t suitable_key_size; | |
d041e4fc LP |
639 | int bits; |
640 | ||
2e64df07 YW |
641 | assert(pkey); |
642 | assert(ret_suitable_key_size); | |
d041e4fc LP |
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) | |
648 | return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key."); | |
649 | ||
7f12adc3 | 650 | bits = EVP_PKEY_bits(pkey); |
d041e4fc LP |
651 | log_debug("Bits in RSA key: %i", bits); |
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; | |
656 | ||
657 | if (suitable_key_size < 1) | |
658 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?"); | |
659 | ||
660 | *ret_suitable_key_size = suitable_key_size; | |
661 | return 0; | |
662 | } | |
7e8facb3 | 663 | |
85686b37 VS |
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. */ | |
dcec950c DS |
666 | int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size, EVP_PKEY **ret) { |
667 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; | |
668 | ||
669 | assert(n); | |
85686b37 | 670 | assert(n_size != 0); |
dcec950c | 671 | assert(e); |
85686b37 | 672 | assert(e_size != 0); |
dcec950c DS |
673 | assert(ret); |
674 | ||
85686b37 VS |
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); | |
dcec950c | 677 | if (!ctx) |
60696b22 | 678 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); |
dcec950c | 679 | |
dcec950c | 680 | if (EVP_PKEY_fromdata_init(ctx) <= 0) |
60696b22 | 681 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); |
dcec950c | 682 | |
85686b37 | 683 | OSSL_PARAM params[3]; |
dcec950c | 684 | |
85686b37 VS |
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); | |
690 | if (!native_n) | |
691 | return log_oom_debug(); | |
dcec950c | 692 | |
85686b37 VS |
693 | _cleanup_free_ void *native_e = memdup_reverse(e, e_size); |
694 | if (!native_e) | |
695 | return log_oom_debug(); | |
dcec950c | 696 | |
85686b37 VS |
697 | params[0] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, native_n, n_size); |
698 | params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, native_e, e_size); | |
699 | #endif | |
700 | params[2] = OSSL_PARAM_construct_end(); | |
dcec950c DS |
701 | |
702 | if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) | |
60696b22 | 703 | return log_openssl_errors("Failed to create RSA EVP_PKEY"); |
dcec950c | 704 | #else |
85686b37 VS |
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 | ||
dcec950c DS |
713 | _cleanup_(RSA_freep) RSA *rsa_key = RSA_new(); |
714 | if (!rsa_key) | |
60696b22 | 715 | return log_openssl_errors("Failed to create new RSA"); |
dcec950c DS |
716 | |
717 | if (!RSA_set0_key(rsa_key, bn_n, bn_e, NULL)) | |
60696b22 | 718 | return log_openssl_errors("Failed to set RSA n/e"); |
dcec950c DS |
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) | |
60696b22 | 725 | return log_openssl_errors("Failed to create new EVP_PKEY"); |
dcec950c DS |
726 | |
727 | if (!EVP_PKEY_assign_RSA(pkey, rsa_key)) | |
60696b22 | 728 | return log_openssl_errors("Failed to assign RSA key"); |
dcec950c DS |
729 | /* pkey owns this now, don't free */ |
730 | TAKE_PTR(rsa_key); | |
731 | #endif | |
732 | ||
733 | *ret = TAKE_PTR(pkey); | |
734 | ||
735 | return 0; | |
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( | |
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); | |
747 | assert(ret_n); | |
748 | assert(ret_n_size); | |
749 | assert(ret_e); | |
750 | assert(ret_e_size); | |
751 | ||
752 | #if OPENSSL_VERSION_MAJOR >= 3 | |
753 | _cleanup_(BN_freep) BIGNUM *bn_n = NULL; | |
754 | if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &bn_n)) | |
60696b22 | 755 | return log_openssl_errors("Failed to get RSA n"); |
dcec950c DS |
756 | |
757 | _cleanup_(BN_freep) BIGNUM *bn_e = NULL; | |
758 | if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &bn_e)) | |
60696b22 | 759 | return log_openssl_errors("Failed to get RSA e"); |
dcec950c DS |
760 | #else |
761 | const RSA *rsa = EVP_PKEY_get0_RSA((EVP_PKEY*) pkey); | |
762 | if (!rsa) | |
60696b22 | 763 | return log_openssl_errors("Failed to get RSA key from public key"); |
dcec950c DS |
764 | |
765 | const BIGNUM *bn_n = RSA_get0_n(rsa); | |
766 | if (!bn_n) | |
60696b22 | 767 | return log_openssl_errors("Failed to get RSA n"); |
dcec950c DS |
768 | |
769 | const BIGNUM *bn_e = RSA_get0_e(rsa); | |
770 | if (!bn_e) | |
60696b22 | 771 | return log_openssl_errors("Failed to get RSA e"); |
dcec950c DS |
772 | #endif |
773 | ||
774 | size_t n_size = BN_num_bytes(bn_n), e_size = BN_num_bytes(bn_e); | |
775 | _cleanup_free_ void *n = malloc(n_size), *e = malloc(e_size); | |
776 | if (!n || !e) | |
777 | return log_oom_debug(); | |
778 | ||
779 | assert(BN_bn2bin(bn_n, n) == (int) n_size); | |
780 | assert(BN_bn2bin(bn_e, e) == (int) e_size); | |
781 | ||
782 | *ret_n = TAKE_PTR(n); | |
783 | *ret_n_size = n_size; | |
784 | *ret_e = TAKE_PTR(e); | |
785 | *ret_e_size = e_size; | |
786 | ||
787 | return 0; | |
788 | } | |
789 | ||
900e73f8 DS |
790 | /* Generate ECC public key from provided curve ID and x/y points. */ |
791 | int ecc_pkey_from_curve_x_y( | |
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); | |
800 | assert(y); | |
801 | assert(ret); | |
802 | ||
803 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); | |
804 | if (!ctx) | |
60696b22 | 805 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); |
900e73f8 | 806 | |
60696b22 DS |
807 | _cleanup_(BN_freep) BIGNUM *bn_x = BN_bin2bn(x, x_size, NULL); |
808 | if (!bn_x) | |
809 | return log_openssl_errors("Failed to create BIGNUM x"); | |
810 | ||
811 | _cleanup_(BN_freep) BIGNUM *bn_y = BN_bin2bn(y, y_size, NULL); | |
812 | if (!bn_y) | |
813 | return log_openssl_errors("Failed to create BIGNUM y"); | |
900e73f8 DS |
814 | |
815 | _cleanup_(EC_GROUP_freep) EC_GROUP *group = EC_GROUP_new_by_curve_name(curve_id); | |
816 | if (!group) | |
60696b22 | 817 | return log_openssl_errors("ECC curve id %d not supported", curve_id); |
900e73f8 DS |
818 | |
819 | _cleanup_(EC_POINT_freep) EC_POINT *point = EC_POINT_new(group); | |
820 | if (!point) | |
60696b22 | 821 | return log_openssl_errors("Failed to create new EC_POINT"); |
900e73f8 DS |
822 | |
823 | if (!EC_POINT_set_affine_coordinates(group, point, bn_x, bn_y, NULL)) | |
60696b22 | 824 | return log_openssl_errors("Failed to set ECC coordinates"); |
900e73f8 DS |
825 | |
826 | #if OPENSSL_VERSION_MAJOR >= 3 | |
827 | if (EVP_PKEY_fromdata_init(ctx) <= 0) | |
60696b22 | 828 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); |
900e73f8 DS |
829 | |
830 | _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); | |
831 | if (!bld) | |
60696b22 | 832 | return log_openssl_errors("Failed to create new OSSL_PARAM_BLD"); |
900e73f8 DS |
833 | |
834 | if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, (char*) OSSL_EC_curve_nid2name(curve_id), 0)) | |
60696b22 | 835 | return log_openssl_errors("Failed to add ECC OSSL_PKEY_PARAM_GROUP_NAME"); |
900e73f8 DS |
836 | |
837 | _cleanup_(OPENSSL_freep) void *pbuf = NULL; | |
838 | size_t pbuf_len = 0; | |
839 | pbuf_len = EC_POINT_point2buf(group, point, POINT_CONVERSION_UNCOMPRESSED, (unsigned char**) &pbuf, NULL); | |
840 | if (pbuf_len == 0) | |
60696b22 | 841 | return log_openssl_errors("Failed to convert ECC point to buffer"); |
900e73f8 DS |
842 | |
843 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pbuf, pbuf_len)) | |
60696b22 | 844 | return log_openssl_errors("Failed to add ECC OSSL_PKEY_PARAM_PUB_KEY"); |
900e73f8 DS |
845 | |
846 | _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); | |
847 | if (!params) | |
60696b22 | 848 | return log_openssl_errors("Failed to build ECC OSSL_PARAM"); |
900e73f8 DS |
849 | |
850 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; | |
851 | if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) | |
60696b22 | 852 | return log_openssl_errors("Failed to create ECC EVP_PKEY"); |
900e73f8 DS |
853 | #else |
854 | _cleanup_(EC_KEY_freep) EC_KEY *eckey = EC_KEY_new(); | |
855 | if (!eckey) | |
60696b22 | 856 | return log_openssl_errors("Failed to create new EC_KEY"); |
900e73f8 DS |
857 | |
858 | if (!EC_KEY_set_group(eckey, group)) | |
60696b22 | 859 | return log_openssl_errors("Failed to set ECC group"); |
900e73f8 DS |
860 | |
861 | if (!EC_KEY_set_public_key(eckey, point)) | |
60696b22 | 862 | return log_openssl_errors("Failed to set ECC point"); |
900e73f8 DS |
863 | |
864 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = EVP_PKEY_new(); | |
865 | if (!pkey) | |
60696b22 | 866 | return log_openssl_errors("Failed to create new EVP_PKEY"); |
900e73f8 DS |
867 | |
868 | if (!EVP_PKEY_assign_EC_KEY(pkey, eckey)) | |
60696b22 | 869 | return log_openssl_errors("Failed to assign ECC key"); |
900e73f8 DS |
870 | /* pkey owns this now, don't free */ |
871 | TAKE_PTR(eckey); | |
872 | #endif | |
873 | ||
874 | *ret = TAKE_PTR(pkey); | |
875 | ||
876 | return 0; | |
877 | } | |
878 | ||
879 | int ecc_pkey_to_curve_x_y( | |
880 | const EVP_PKEY *pkey, | |
881 | int *ret_curve_id, | |
882 | void **ret_x, | |
883 | size_t *ret_x_size, | |
884 | void **ret_y, | |
885 | size_t *ret_y_size) { | |
886 | ||
887 | _cleanup_(BN_freep) BIGNUM *bn_x = NULL, *bn_y = NULL; | |
888 | int curve_id; | |
889 | ||
890 | assert(pkey); | |
891 | ||
892 | #if OPENSSL_VERSION_MAJOR >= 3 | |
893 | size_t name_size; | |
894 | if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0, &name_size)) | |
60696b22 | 895 | return log_openssl_errors("Failed to get ECC group name size"); |
900e73f8 | 896 | |
b0307102 | 897 | _cleanup_free_ char *name = new(char, name_size + 1); |
900e73f8 DS |
898 | if (!name) |
899 | return log_oom_debug(); | |
900 | ||
901 | if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, name, name_size + 1, NULL)) | |
60696b22 | 902 | return log_openssl_errors("Failed to get ECC group name"); |
900e73f8 DS |
903 | |
904 | curve_id = OBJ_sn2nid(name); | |
905 | if (curve_id == NID_undef) | |
60696b22 | 906 | return log_openssl_errors("Failed to get ECC curve id"); |
900e73f8 DS |
907 | |
908 | if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &bn_x)) | |
60696b22 | 909 | return log_openssl_errors("Failed to get ECC point x"); |
900e73f8 DS |
910 | |
911 | if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &bn_y)) | |
60696b22 | 912 | return log_openssl_errors("Failed to get ECC point y"); |
900e73f8 DS |
913 | #else |
914 | const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY*) pkey); | |
915 | if (!eckey) | |
60696b22 | 916 | return log_openssl_errors("Failed to get EC_KEY"); |
900e73f8 DS |
917 | |
918 | const EC_GROUP *group = EC_KEY_get0_group(eckey); | |
919 | if (!group) | |
60696b22 | 920 | return log_openssl_errors("Failed to get EC_GROUP"); |
900e73f8 DS |
921 | |
922 | curve_id = EC_GROUP_get_curve_name(group); | |
923 | if (curve_id == NID_undef) | |
60696b22 | 924 | return log_openssl_errors("Failed to get ECC curve id"); |
900e73f8 DS |
925 | |
926 | const EC_POINT *point = EC_KEY_get0_public_key(eckey); | |
927 | if (!point) | |
60696b22 | 928 | return log_openssl_errors("Failed to get EC_POINT"); |
900e73f8 DS |
929 | |
930 | bn_x = BN_new(); | |
931 | bn_y = BN_new(); | |
932 | if (!bn_x || !bn_y) | |
60696b22 | 933 | return log_openssl_errors("Failed to create new BIGNUM"); |
900e73f8 DS |
934 | |
935 | if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y, NULL)) | |
60696b22 | 936 | return log_openssl_errors("Failed to get ECC x/y."); |
900e73f8 DS |
937 | #endif |
938 | ||
939 | size_t x_size = BN_num_bytes(bn_x), y_size = BN_num_bytes(bn_y); | |
940 | _cleanup_free_ void *x = malloc(x_size), *y = malloc(y_size); | |
941 | if (!x || !y) | |
942 | return log_oom_debug(); | |
943 | ||
944 | assert(BN_bn2bin(bn_x, x) == (int) x_size); | |
945 | assert(BN_bn2bin(bn_y, y) == (int) y_size); | |
946 | ||
947 | if (ret_curve_id) | |
948 | *ret_curve_id = curve_id; | |
949 | if (ret_x) | |
950 | *ret_x = TAKE_PTR(x); | |
951 | if (ret_x_size) | |
952 | *ret_x_size = x_size; | |
953 | if (ret_y) | |
954 | *ret_y = TAKE_PTR(y); | |
955 | if (ret_y_size) | |
956 | *ret_y_size = y_size; | |
957 | ||
958 | return 0; | |
959 | } | |
960 | ||
961 | /* Generate a new ECC key for the specified ECC curve id. */ | |
962 | int ecc_pkey_new(int curve_id, EVP_PKEY **ret) { | |
963 | assert(ret); | |
964 | ||
965 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); | |
966 | if (!ctx) | |
60696b22 | 967 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); |
900e73f8 DS |
968 | |
969 | if (EVP_PKEY_keygen_init(ctx) <= 0) | |
60696b22 | 970 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); |
900e73f8 DS |
971 | |
972 | if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curve_id) <= 0) | |
60696b22 | 973 | return log_openssl_errors("Failed to set ECC curve %d", curve_id); |
900e73f8 DS |
974 | |
975 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; | |
976 | if (EVP_PKEY_keygen(ctx, &pkey) <= 0) | |
60696b22 | 977 | return log_openssl_errors("Failed to generate ECC key"); |
900e73f8 DS |
978 | |
979 | *ret = TAKE_PTR(pkey); | |
980 | ||
981 | return 0; | |
982 | } | |
983 | ||
779b80d8 DS |
984 | /* Perform ECDH to derive an ECC shared secret between the provided private key and public peer key. For two |
985 | * keys, this will result in the same shared secret in either direction; ECDH using Alice's private key and | |
986 | * Bob's public (peer) key will result in the same shared secret as ECDH using Bob's private key and Alice's | |
987 | * public (peer) key. On success, this returns 0 and provides the shared secret; otherwise this returns an | |
988 | * error. */ | |
989 | int ecc_ecdh(const EVP_PKEY *private_pkey, | |
990 | const EVP_PKEY *peer_pkey, | |
991 | void **ret_shared_secret, | |
992 | size_t *ret_shared_secret_size) { | |
993 | ||
994 | assert(private_pkey); | |
995 | assert(peer_pkey); | |
996 | assert(ret_shared_secret); | |
997 | assert(ret_shared_secret_size); | |
998 | ||
999 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) private_pkey, NULL); | |
1000 | if (!ctx) | |
1001 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); | |
1002 | ||
1003 | if (EVP_PKEY_derive_init(ctx) <= 0) | |
1004 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); | |
1005 | ||
1006 | if (EVP_PKEY_derive_set_peer(ctx, (EVP_PKEY*) peer_pkey) <= 0) | |
1007 | return log_openssl_errors("Failed to set ECC derive peer"); | |
1008 | ||
1009 | size_t shared_secret_size; | |
1010 | if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0) | |
1011 | return log_openssl_errors("Failed to get ECC shared secret size"); | |
1012 | ||
3d05c058 | 1013 | _cleanup_(erase_and_freep) void *shared_secret = malloc(shared_secret_size); |
779b80d8 DS |
1014 | if (!shared_secret) |
1015 | return log_oom_debug(); | |
1016 | ||
1017 | if (EVP_PKEY_derive(ctx, (unsigned char*) shared_secret, &shared_secret_size) <= 0) | |
1018 | return log_openssl_errors("Failed to derive ECC shared secret"); | |
1019 | ||
1020 | *ret_shared_secret = TAKE_PTR(shared_secret); | |
1021 | *ret_shared_secret_size = shared_secret_size; | |
1022 | ||
1023 | return 0; | |
1024 | } | |
1025 | ||
e8ccb5c7 LP |
1026 | int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) { |
1027 | _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL; | |
1028 | _cleanup_free_ void *d = NULL, *h = NULL; | |
1029 | int sz, lsz, msz; | |
1030 | unsigned umsz; | |
1031 | unsigned char *dd; | |
1032 | ||
1033 | /* Calculates a message digest of the DER encoded public key */ | |
1034 | ||
1035 | assert(pk); | |
1036 | assert(md); | |
1037 | assert(ret); | |
1038 | assert(ret_size); | |
1039 | ||
1040 | sz = i2d_PublicKey(pk, NULL); | |
1041 | if (sz < 0) | |
60696b22 | 1042 | return log_openssl_errors("Unable to convert public key to DER format"); |
e8ccb5c7 LP |
1043 | |
1044 | dd = d = malloc(sz); | |
1045 | if (!d) | |
1046 | return log_oom_debug(); | |
1047 | ||
1048 | lsz = i2d_PublicKey(pk, &dd); | |
1049 | if (lsz < 0) | |
60696b22 | 1050 | return log_openssl_errors("Unable to convert public key to DER format"); |
e8ccb5c7 LP |
1051 | |
1052 | m = EVP_MD_CTX_new(); | |
1053 | if (!m) | |
60696b22 | 1054 | return log_openssl_errors("Failed to create new EVP_MD_CTX"); |
e8ccb5c7 LP |
1055 | |
1056 | if (EVP_DigestInit_ex(m, md, NULL) != 1) | |
60696b22 | 1057 | return log_openssl_errors("Failed to initialize %s context", EVP_MD_name(md)); |
e8ccb5c7 LP |
1058 | |
1059 | if (EVP_DigestUpdate(m, d, lsz) != 1) | |
60696b22 | 1060 | return log_openssl_errors("Failed to run %s context", EVP_MD_name(md)); |
e8ccb5c7 LP |
1061 | |
1062 | msz = EVP_MD_size(md); | |
2e64df07 | 1063 | assert(msz > 0); |
e8ccb5c7 LP |
1064 | |
1065 | h = malloc(msz); | |
1066 | if (!h) | |
1067 | return log_oom_debug(); | |
1068 | ||
1069 | umsz = msz; | |
1070 | if (EVP_DigestFinal_ex(m, h, &umsz) != 1) | |
60696b22 | 1071 | return log_openssl_errors("Failed to finalize hash context"); |
e8ccb5c7 | 1072 | |
2e64df07 | 1073 | assert(umsz == (unsigned) msz); |
e8ccb5c7 LP |
1074 | |
1075 | *ret = TAKE_PTR(h); | |
1076 | *ret_size = msz; | |
1077 | ||
1078 | return 0; | |
1079 | } | |
1080 | ||
ef65c0f6 LP |
1081 | int digest_and_sign( |
1082 | const EVP_MD *md, | |
1083 | EVP_PKEY *privkey, | |
1084 | const void *data, size_t size, | |
1085 | void **ret, size_t *ret_size) { | |
1086 | ||
1144f07d LP |
1087 | int r; |
1088 | ||
ef65c0f6 LP |
1089 | assert(privkey); |
1090 | assert(ret); | |
1091 | assert(ret_size); | |
1092 | ||
1093 | if (size == 0) | |
1094 | data = ""; /* make sure to pass a valid pointer to OpenSSL */ | |
1095 | else { | |
1096 | assert(data); | |
1097 | ||
1098 | if (size == SIZE_MAX) /* If SIZE_MAX input is a string whose size we determine automatically */ | |
1099 | size = strlen(data); | |
1100 | } | |
1101 | ||
1102 | _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); | |
1103 | if (!mdctx) | |
1104 | return log_openssl_errors("Failed to create new EVP_MD_CTX"); | |
1105 | ||
1144f07d LP |
1106 | if (EVP_DigestSignInit(mdctx, NULL, md, NULL, privkey) != 1) { |
1107 | /* Distro security policies often disable support for SHA-1. Let's return a recognizable | |
1108 | * error for that case. */ | |
1109 | bool invalid_digest = ERR_GET_REASON(ERR_peek_last_error()) == EVP_R_INVALID_DIGEST; | |
1110 | r = log_openssl_errors("Failed to initialize signature context"); | |
1111 | return invalid_digest ? -EADDRNOTAVAIL : r; | |
1112 | } | |
ef65c0f6 LP |
1113 | |
1114 | /* Determine signature size */ | |
1115 | size_t ss; | |
1116 | if (EVP_DigestSign(mdctx, NULL, &ss, data, size) != 1) | |
1117 | return log_openssl_errors("Failed to determine size of signature"); | |
1118 | ||
1119 | _cleanup_free_ void *sig = malloc(ss); | |
1120 | if (!sig) | |
1121 | return log_oom_debug(); | |
1122 | ||
1123 | if (EVP_DigestSign(mdctx, sig, &ss, data, size) != 1) | |
1124 | return log_openssl_errors("Failed to sign data"); | |
1125 | ||
1126 | *ret = TAKE_PTR(sig); | |
1127 | *ret_size = ss; | |
1128 | return 0; | |
1129 | } | |
1130 | ||
768a297c | 1131 | int pkcs7_new(X509 *certificate, EVP_PKEY *private_key, const char *hash_algorithm, PKCS7 **ret_p7, PKCS7_SIGNER_INFO **ret_si) { |
d46b16af DDM |
1132 | assert(certificate); |
1133 | assert(ret_p7); | |
1134 | ||
1135 | /* This function sets up a new PKCS7 signing context. If a private key is provided, the context is | |
1136 | * set up for "in-band" signing with PKCS7_dataFinal(). If a private key is not provided, the context | |
1137 | * is set up for "out-of-band" signing, meaning the signature has to be provided by the user and | |
768a297c DS |
1138 | * copied into the signer info's "enc_digest" field. If the signing hash algorithm is not provided, |
1139 | * SHA-256 is used. */ | |
d46b16af DDM |
1140 | |
1141 | _cleanup_(PKCS7_freep) PKCS7 *p7 = PKCS7_new(); | |
1142 | if (!p7) | |
1143 | return log_oom(); | |
1144 | ||
1145 | if (PKCS7_set_type(p7, NID_pkcs7_signed) == 0) | |
1146 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 type: %s", | |
1147 | ERR_error_string(ERR_get_error(), NULL)); | |
1148 | ||
1149 | if (PKCS7_content_new(p7, NID_pkcs7_data) == 0) | |
1150 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 content: %s", | |
1151 | ERR_error_string(ERR_get_error(), NULL)); | |
1152 | ||
1153 | if (PKCS7_add_certificate(p7, certificate) == 0) | |
1154 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 certificate: %s", | |
1155 | ERR_error_string(ERR_get_error(), NULL)); | |
1156 | ||
768a297c DS |
1157 | int x509_pknid = 0; |
1158 | if (X509_get_signature_info(certificate, NULL, &x509_pknid, NULL, NULL) == 0) | |
d46b16af DDM |
1159 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get X509 digest NID: %s", |
1160 | ERR_error_string(ERR_get_error(), NULL)); | |
1161 | ||
768a297c | 1162 | const EVP_MD *md = EVP_get_digestbyname(hash_algorithm ?: "SHA256"); |
d46b16af | 1163 | if (!md) |
768a297c DS |
1164 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to get digest algorithm '%s'", |
1165 | hash_algorithm ?: "SHA256"); | |
d46b16af DDM |
1166 | |
1167 | _cleanup_(PKCS7_SIGNER_INFO_freep) PKCS7_SIGNER_INFO *si = PKCS7_SIGNER_INFO_new(); | |
1168 | if (!si) | |
1169 | return log_oom(); | |
1170 | ||
1171 | if (private_key) { | |
768a297c | 1172 | if (PKCS7_SIGNER_INFO_set(si, certificate, private_key, md) <= 0) |
d46b16af DDM |
1173 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to configure signer info: %s", |
1174 | ERR_error_string(ERR_get_error(), NULL)); | |
1175 | } else { | |
1176 | if (ASN1_INTEGER_set(si->version, 1) == 0) | |
1177 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info version: %s", | |
1178 | ERR_error_string(ERR_get_error(), NULL)); | |
1179 | ||
1180 | if (X509_NAME_set(&si->issuer_and_serial->issuer, X509_get_issuer_name(certificate)) == 0) | |
1181 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info issuer: %s", | |
1182 | ERR_error_string(ERR_get_error(), NULL)); | |
1183 | ||
1184 | ASN1_INTEGER_free(si->issuer_and_serial->serial); | |
1185 | si->issuer_and_serial->serial = ASN1_INTEGER_dup(X509_get0_serialNumber(certificate)); | |
1186 | if (!si->issuer_and_serial->serial) | |
1187 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info serial: %s", | |
1188 | ERR_error_string(ERR_get_error(), NULL)); | |
1189 | ||
768a297c | 1190 | if (X509_ALGOR_set0(si->digest_alg, OBJ_nid2obj(EVP_MD_type(md)), V_ASN1_NULL, NULL) == 0) |
d46b16af DDM |
1191 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info digest algorithm: %s", |
1192 | ERR_error_string(ERR_get_error(), NULL)); | |
1193 | ||
1194 | if (X509_ALGOR_set0(si->digest_enc_alg, OBJ_nid2obj(x509_pknid), V_ASN1_NULL, NULL) == 0) | |
1195 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set signer info signing algorithm: %s", | |
1196 | ERR_error_string(ERR_get_error(), NULL)); | |
1197 | } | |
1198 | ||
1199 | if (PKCS7_add_signer(p7, si) == 0) | |
1200 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to set PKCS7 signer info: %s", | |
1201 | ERR_error_string(ERR_get_error(), NULL)); | |
1202 | ||
1203 | *ret_p7 = TAKE_PTR(p7); | |
1204 | if (ret_si) | |
1205 | /* We do not pass ownership here, 'si' object remains owned by 'p7' object. */ | |
1206 | *ret_si = si; | |
1207 | ||
1208 | TAKE_PTR(si); | |
1209 | ||
1210 | return 0; | |
1211 | } | |
1212 | ||
7e8facb3 ZJS |
1213 | int string_hashsum( |
1214 | const char *s, | |
1215 | size_t len, | |
11f7bc5e | 1216 | const char *md_algorithm, |
7e8facb3 ZJS |
1217 | char **ret) { |
1218 | ||
11f7bc5e | 1219 | _cleanup_free_ void *hash = NULL; |
7e8facb3 | 1220 | size_t hash_size; |
38e1035b | 1221 | _cleanup_free_ char *enc = NULL; |
7e8facb3 ZJS |
1222 | int r; |
1223 | ||
11f7bc5e DS |
1224 | assert(s || len == 0); |
1225 | assert(md_algorithm); | |
1226 | assert(ret); | |
7e8facb3 | 1227 | |
11f7bc5e | 1228 | r = openssl_digest(md_algorithm, s, len, &hash, &hash_size); |
7e8facb3 ZJS |
1229 | if (r < 0) |
1230 | return r; | |
1231 | ||
1232 | enc = hexmem(hash, hash_size); | |
1233 | if (!enc) | |
1234 | return -ENOMEM; | |
1235 | ||
11f7bc5e | 1236 | *ret = TAKE_PTR(enc); |
7e8facb3 | 1237 | return 0; |
7e8facb3 | 1238 | } |
876206f2 | 1239 | |
3d05c058 VS |
1240 | static int ecc_pkey_generate_volume_keys( |
1241 | EVP_PKEY *pkey, | |
1242 | void **ret_decrypted_key, | |
1243 | size_t *ret_decrypted_key_size, | |
1244 | void **ret_saved_key, | |
1245 | size_t *ret_saved_key_size) { | |
1246 | ||
1247 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_new = NULL; | |
1248 | _cleanup_(erase_and_freep) void *decrypted_key = NULL; | |
1249 | _cleanup_free_ unsigned char *saved_key = NULL; | |
1250 | size_t decrypted_key_size, saved_key_size; | |
1251 | int nid = NID_undef; | |
1252 | int r; | |
1253 | ||
1254 | #if OPENSSL_VERSION_MAJOR >= 3 | |
1255 | _cleanup_free_ char *curve_name = NULL; | |
1256 | size_t len = 0; | |
1257 | ||
1258 | if (EVP_PKEY_get_group_name(pkey, NULL, 0, &len) != 1 || len == 0) | |
1259 | return log_openssl_errors("Failed to determine PKEY group name length"); | |
1260 | ||
1261 | len++; | |
1262 | curve_name = new(char, len); | |
1263 | if (!curve_name) | |
1264 | return log_oom_debug(); | |
1265 | ||
1266 | if (EVP_PKEY_get_group_name(pkey, curve_name, len, &len) != 1) | |
1267 | return log_openssl_errors("Failed to get PKEY group name"); | |
1268 | ||
1269 | nid = OBJ_sn2nid(curve_name); | |
1270 | #else | |
1271 | EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); | |
1272 | if (!ec_key) | |
1273 | return log_openssl_errors("PKEY doesn't have EC_KEY associated"); | |
1274 | ||
1275 | if (EC_KEY_check_key(ec_key) != 1) | |
1276 | return log_openssl_errors("EC_KEY associated with PKEY is not valid"); | |
1277 | ||
1278 | nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); | |
1279 | #endif | |
1280 | ||
1281 | r = ecc_pkey_new(nid, &pkey_new); | |
1282 | if (r < 0) | |
1283 | return log_debug_errno(r, "Failed to generate a new EC keypair: %m"); | |
1284 | ||
1285 | r = ecc_ecdh(pkey_new, pkey, &decrypted_key, &decrypted_key_size); | |
1286 | if (r < 0) | |
1287 | return log_debug_errno(r, "Failed to derive shared secret: %m"); | |
1288 | ||
1289 | #if OPENSSL_VERSION_MAJOR >= 3 | |
1290 | /* EVP_PKEY_get1_encoded_public_key() always returns uncompressed format of EC points. | |
1291 | See https://github.com/openssl/openssl/discussions/22835 */ | |
1292 | saved_key_size = EVP_PKEY_get1_encoded_public_key(pkey_new, &saved_key); | |
1293 | if (saved_key_size == 0) | |
1294 | return log_openssl_errors("Failed to convert the generated public key to SEC1 format"); | |
1295 | #else | |
1296 | EC_KEY *ec_key_new = EVP_PKEY_get0_EC_KEY(pkey_new); | |
1297 | if (!ec_key_new) | |
1298 | return log_openssl_errors("The generated key doesn't have associated EC_KEY"); | |
1299 | ||
1300 | if (EC_KEY_check_key(ec_key_new) != 1) | |
1301 | return log_openssl_errors("EC_KEY associated with the generated key is not valid"); | |
1302 | ||
1303 | saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new), | |
1304 | EC_KEY_get0_public_key(ec_key_new), | |
1305 | POINT_CONVERSION_UNCOMPRESSED, | |
1306 | NULL, 0, NULL); | |
1307 | if (saved_key_size == 0) | |
1308 | return log_openssl_errors("Failed to determine size of the generated public key"); | |
1309 | ||
1310 | saved_key = malloc(saved_key_size); | |
1311 | if (!saved_key) | |
1312 | return log_oom_debug(); | |
1313 | ||
1314 | saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new), | |
1315 | EC_KEY_get0_public_key(ec_key_new), | |
1316 | POINT_CONVERSION_UNCOMPRESSED, | |
1317 | saved_key, saved_key_size, NULL); | |
1318 | if (saved_key_size == 0) | |
1319 | return log_openssl_errors("Failed to convert the generated public key to SEC1 format"); | |
1320 | #endif | |
1321 | ||
1322 | *ret_decrypted_key = TAKE_PTR(decrypted_key); | |
1323 | *ret_decrypted_key_size = decrypted_key_size; | |
1324 | *ret_saved_key = TAKE_PTR(saved_key); | |
1325 | *ret_saved_key_size = saved_key_size; | |
1326 | return 0; | |
1327 | } | |
1328 | ||
876206f2 VS |
1329 | static int rsa_pkey_generate_volume_keys( |
1330 | EVP_PKEY *pkey, | |
1331 | void **ret_decrypted_key, | |
1332 | size_t *ret_decrypted_key_size, | |
1333 | void **ret_saved_key, | |
1334 | size_t *ret_saved_key_size) { | |
1335 | ||
1336 | _cleanup_(erase_and_freep) void *decrypted_key = NULL; | |
1337 | _cleanup_free_ void *saved_key = NULL; | |
1338 | size_t decrypted_key_size, saved_key_size; | |
1339 | int r; | |
1340 | ||
1341 | r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size); | |
1342 | if (r < 0) | |
1343 | return log_debug_errno(r, "Failed to determine RSA public key size."); | |
1344 | ||
1345 | log_debug("Generating %zu bytes random key.", decrypted_key_size); | |
1346 | ||
1347 | decrypted_key = malloc(decrypted_key_size); | |
1348 | if (!decrypted_key) | |
1349 | return log_oom_debug(); | |
1350 | ||
1351 | r = crypto_random_bytes(decrypted_key, decrypted_key_size); | |
1352 | if (r < 0) | |
1353 | return log_debug_errno(r, "Failed to generate random key: %m"); | |
1354 | ||
1355 | r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &saved_key, &saved_key_size); | |
1356 | if (r < 0) | |
1357 | return log_debug_errno(r, "Failed to encrypt random key: %m"); | |
1358 | ||
1359 | *ret_decrypted_key = TAKE_PTR(decrypted_key); | |
1360 | *ret_decrypted_key_size = decrypted_key_size; | |
1361 | *ret_saved_key = TAKE_PTR(saved_key); | |
1362 | *ret_saved_key_size = saved_key_size; | |
1363 | return 0; | |
1364 | } | |
1365 | ||
85686b37 VS |
1366 | int pkey_generate_volume_keys( |
1367 | EVP_PKEY *pkey, | |
876206f2 VS |
1368 | void **ret_decrypted_key, |
1369 | size_t *ret_decrypted_key_size, | |
1370 | void **ret_saved_key, | |
1371 | size_t *ret_saved_key_size) { | |
1372 | ||
85686b37 | 1373 | assert(pkey); |
876206f2 VS |
1374 | assert(ret_decrypted_key); |
1375 | assert(ret_decrypted_key_size); | |
1376 | assert(ret_saved_key); | |
1377 | assert(ret_saved_key_size); | |
1378 | ||
876206f2 VS |
1379 | #if OPENSSL_VERSION_MAJOR >= 3 |
1380 | int type = EVP_PKEY_get_base_id(pkey); | |
1381 | #else | |
1382 | int type = EVP_PKEY_base_id(pkey); | |
1383 | #endif | |
1384 | switch (type) { | |
1385 | ||
1386 | case EVP_PKEY_RSA: | |
1387 | return rsa_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size); | |
1388 | ||
3d05c058 VS |
1389 | case EVP_PKEY_EC: |
1390 | return ecc_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size); | |
1391 | ||
876206f2 | 1392 | case NID_undef: |
4e494e6a | 1393 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine a type of public key."); |
876206f2 VS |
1394 | |
1395 | default: | |
1396 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported public key type: %s", OBJ_nid2sn(type)); | |
1397 | } | |
1398 | } | |
dba0afa1 | 1399 | |
eac5336c DDM |
1400 | static int load_key_from_provider( |
1401 | const char *provider, | |
1402 | const char *private_key_uri, | |
eac5336c | 1403 | EVP_PKEY **ret) { |
dba0afa1 LB |
1404 | |
1405 | assert(provider); | |
1406 | assert(private_key_uri); | |
1407 | assert(ret); | |
1408 | ||
1409 | #if OPENSSL_VERSION_MAJOR >= 3 | |
1410 | /* Load the provider so that this can work without any custom written configuration in /etc/. | |
1411 | * Also load the 'default' as that seems to be the recommendation. */ | |
1412 | if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true)) | |
1413 | return log_openssl_errors("Failed to load OpenSSL provider '%s'", provider); | |
1414 | if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true)) | |
1415 | return log_openssl_errors("Failed to load OpenSSL provider 'default'"); | |
1416 | ||
1417 | _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open( | |
1418 | private_key_uri, | |
0bf70b19 DDM |
1419 | /*ui_method=*/ NULL, |
1420 | /*ui_method=*/ NULL, | |
dba0afa1 LB |
1421 | /* post_process= */ NULL, |
1422 | /* post_process_data= */ NULL); | |
1423 | if (!store) | |
1424 | return log_openssl_errors("Failed to open OpenSSL store via '%s'", private_key_uri); | |
1425 | ||
5619a618 DDM |
1426 | if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) == 0) |
1427 | return log_openssl_errors("Failed to filter store by private keys"); | |
1428 | ||
dba0afa1 LB |
1429 | _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store); |
1430 | if (!info) | |
1431 | return log_openssl_errors("Failed to load OpenSSL store via '%s'", private_key_uri); | |
1432 | ||
1433 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = OSSL_STORE_INFO_get1_PKEY(info); | |
1434 | if (!private_key) | |
1435 | return log_openssl_errors("Failed to load private key via '%s'", private_key_uri); | |
1436 | ||
1437 | *ret = TAKE_PTR(private_key); | |
1438 | ||
1439 | return 0; | |
1440 | #else | |
1441 | return -EOPNOTSUPP; | |
1442 | #endif | |
1443 | } | |
1444 | ||
0bf70b19 | 1445 | static int load_key_from_engine(const char *engine, const char *private_key_uri, EVP_PKEY **ret) { |
dba0afa1 LB |
1446 | assert(engine); |
1447 | assert(private_key_uri); | |
1448 | assert(ret); | |
1449 | ||
81d61d6a | 1450 | #if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) |
dba0afa1 LB |
1451 | DISABLE_WARNING_DEPRECATED_DECLARATIONS; |
1452 | _cleanup_(ENGINE_freep) ENGINE *e = ENGINE_by_id(engine); | |
1453 | if (!e) | |
1454 | return log_openssl_errors("Failed to load signing engine '%s'", engine); | |
1455 | ||
1456 | if (ENGINE_init(e) == 0) | |
1457 | return log_openssl_errors("Failed to initialize signing engine '%s'", engine); | |
1458 | ||
0bf70b19 | 1459 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = ENGINE_load_private_key(e, private_key_uri, /*ui_method=*/ NULL, /*callback_data=*/ NULL); |
dba0afa1 LB |
1460 | if (!private_key) |
1461 | return log_openssl_errors("Failed to load private key from '%s'", private_key_uri); | |
1462 | REENABLE_WARNING; | |
1463 | ||
1464 | *ret = TAKE_PTR(private_key); | |
1465 | ||
1466 | return 0; | |
81d61d6a NL |
1467 | #else |
1468 | return -EOPNOTSUPP; | |
1469 | #endif | |
dba0afa1 LB |
1470 | } |
1471 | ||
d1d16c62 | 1472 | #ifndef OPENSSL_NO_UI_CONSOLE |
eac5336c DDM |
1473 | static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) { |
1474 | int r; | |
1475 | ||
1476 | switch(UI_get_string_type(uis)) { | |
1477 | case UIT_PROMPT: { | |
1478 | /* If no ask password request was configured use the default openssl UI. */ | |
0bf70b19 | 1479 | AskPasswordRequest *req = (AskPasswordRequest*) UI_method_get_ex_data(UI_get_method(ui), 0); |
eac5336c DDM |
1480 | if (!req) |
1481 | return (UI_method_get_reader(UI_OpenSSL()))(ui, uis); | |
1482 | ||
1483 | req->message = UI_get0_output_string(uis); | |
1484 | ||
fe5a1afb | 1485 | _cleanup_strv_free_ char **l = NULL; |
c4a02a52 | 1486 | r = ask_password_auto(req, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &l); |
eac5336c DDM |
1487 | if (r < 0) { |
1488 | log_error_errno(r, "Failed to query for PIN: %m"); | |
1489 | return 0; | |
1490 | } | |
1491 | ||
1492 | if (strv_length(l) != 1) { | |
1493 | log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected only a single password/pin."); | |
1494 | return 0; | |
1495 | } | |
1496 | ||
1497 | if (UI_set_result(ui, uis, *l) != 0) { | |
1498 | log_openssl_errors("Failed to set user interface result"); | |
1499 | return 0; | |
1500 | } | |
1501 | ||
1502 | return 1; | |
1503 | } | |
1504 | default: | |
1505 | return (UI_method_get_reader(UI_OpenSSL()))(ui, uis); | |
1506 | } | |
1507 | } | |
d1d16c62 | 1508 | #endif |
8939d335 | 1509 | |
0bf70b19 DDM |
1510 | static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) { |
1511 | _cleanup_(erase_and_freep) char *rawkey = NULL; | |
1512 | _cleanup_(BIO_freep) BIO *kb = NULL; | |
1513 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL; | |
1514 | size_t rawkeysz; | |
1515 | int r; | |
1516 | ||
1517 | assert(path); | |
1518 | assert(ret); | |
1519 | ||
1520 | r = read_full_file_full( | |
1521 | AT_FDCWD, path, UINT64_MAX, SIZE_MAX, | |
1522 | READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, | |
1523 | NULL, | |
1524 | &rawkey, &rawkeysz); | |
1525 | if (r < 0) | |
1526 | return log_debug_errno(r, "Failed to read key file '%s': %m", path); | |
1527 | ||
1528 | kb = BIO_new_mem_buf(rawkey, rawkeysz); | |
1529 | if (!kb) | |
1530 | return log_oom_debug(); | |
1531 | ||
1532 | pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL); | |
1533 | if (!pk) | |
1534 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s", | |
1535 | ERR_error_string(ERR_get_error(), NULL)); | |
1536 | ||
1537 | if (ret) | |
1538 | *ret = TAKE_PTR(pk); | |
1539 | ||
1540 | return 0; | |
1541 | } | |
1542 | ||
1543 | static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSSLAskPasswordUI **ret) { | |
eac5336c DDM |
1544 | assert(ret); |
1545 | ||
d1d16c62 | 1546 | #ifndef OPENSSL_NO_UI_CONSOLE |
eac5336c DDM |
1547 | _cleanup_(UI_destroy_methodp) UI_METHOD *method = UI_create_method("systemd-ask-password"); |
1548 | if (!method) | |
1549 | return log_openssl_errors("Failed to initialize openssl user interface"); | |
1550 | ||
1551 | if (UI_method_set_reader(method, openssl_ask_password_ui_read) != 0) | |
1552 | return log_openssl_errors("Failed to set openssl user interface reader"); | |
1553 | ||
1554 | OpenSSLAskPasswordUI *ui = new(OpenSSLAskPasswordUI, 1); | |
1555 | if (!ui) | |
1556 | return log_oom_debug(); | |
1557 | ||
1558 | *ui = (OpenSSLAskPasswordUI) { | |
1559 | .method = TAKE_PTR(method), | |
0bf70b19 | 1560 | .request = *request, |
eac5336c DDM |
1561 | }; |
1562 | ||
0bf70b19 DDM |
1563 | UI_set_default_method(ui->method); |
1564 | ||
1565 | if (UI_method_set_ex_data(ui->method, 0, &ui->request) == 0) | |
1566 | return log_openssl_errors("Failed to set extra data for UI method"); | |
1567 | ||
eac5336c DDM |
1568 | *ret = TAKE_PTR(ui); |
1569 | return 0; | |
d1d16c62 YW |
1570 | #else |
1571 | return -EOPNOTSUPP; | |
1572 | #endif | |
0bf70b19 | 1573 | } |
a1d46e30 DDM |
1574 | |
1575 | static int load_x509_certificate_from_file(const char *path, X509 **ret) { | |
1576 | _cleanup_free_ char *rawcert = NULL; | |
1577 | _cleanup_(X509_freep) X509 *cert = NULL; | |
1578 | _cleanup_(BIO_freep) BIO *cb = NULL; | |
1579 | size_t rawcertsz; | |
1580 | int r; | |
1581 | ||
1582 | assert(path); | |
1583 | assert(ret); | |
1584 | ||
1585 | r = read_full_file_full( | |
1586 | AT_FDCWD, path, UINT64_MAX, SIZE_MAX, | |
1587 | READ_FULL_FILE_CONNECT_SOCKET, | |
1588 | NULL, | |
1589 | &rawcert, &rawcertsz); | |
1590 | if (r < 0) | |
1591 | return log_debug_errno(r, "Failed to read certificate file '%s': %m", path); | |
1592 | ||
1593 | cb = BIO_new_mem_buf(rawcert, rawcertsz); | |
1594 | if (!cb) | |
1595 | return log_oom_debug(); | |
1596 | ||
1597 | cert = PEM_read_bio_X509(cb, NULL, NULL, NULL); | |
1598 | if (!cert) | |
1599 | return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s", | |
1600 | ERR_error_string(ERR_get_error(), NULL)); | |
1601 | ||
1602 | if (ret) | |
1603 | *ret = TAKE_PTR(cert); | |
1604 | ||
1605 | return 0; | |
1606 | } | |
1607 | ||
1608 | static int load_x509_certificate_from_provider(const char *provider, const char *certificate_uri, X509 **ret) { | |
1609 | assert(provider); | |
1610 | assert(certificate_uri); | |
1611 | assert(ret); | |
1612 | ||
1613 | #if OPENSSL_VERSION_MAJOR >= 3 | |
1614 | /* Load the provider so that this can work without any custom written configuration in /etc/. | |
1615 | * Also load the 'default' as that seems to be the recommendation. */ | |
1616 | if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true)) | |
1617 | return log_openssl_errors("Failed to load OpenSSL provider '%s'", provider); | |
1618 | if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true)) | |
1619 | return log_openssl_errors("Failed to load OpenSSL provider 'default'"); | |
1620 | ||
1621 | _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open( | |
1622 | certificate_uri, | |
1623 | /*ui_method=*/ NULL, | |
1624 | /*ui_method=*/ NULL, | |
1625 | /* post_process= */ NULL, | |
1626 | /* post_process_data= */ NULL); | |
1627 | if (!store) | |
1628 | return log_openssl_errors("Failed to open OpenSSL store via '%s'", certificate_uri); | |
1629 | ||
1630 | if (OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) == 0) | |
1631 | return log_openssl_errors("Failed to filter store by X.509 certificates"); | |
1632 | ||
1633 | _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store); | |
1634 | if (!info) | |
1635 | return log_openssl_errors("Failed to load OpenSSL store via '%s'", certificate_uri); | |
1636 | ||
1637 | _cleanup_(X509_freep) X509 *cert = OSSL_STORE_INFO_get1_CERT(info); | |
1638 | if (!cert) | |
1639 | return log_openssl_errors("Failed to load certificate via '%s'", certificate_uri); | |
1640 | ||
1641 | *ret = TAKE_PTR(cert); | |
1642 | ||
1643 | return 0; | |
1644 | #else | |
1645 | return -EOPNOTSUPP; | |
1646 | #endif | |
1647 | } | |
0bf70b19 DDM |
1648 | #endif |
1649 | ||
1650 | OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) { | |
d1d16c62 | 1651 | #if HAVE_OPENSSL && !defined(OPENSSL_NO_UI_CONSOLE) |
0bf70b19 DDM |
1652 | if (!ui) |
1653 | return NULL; | |
1654 | ||
1655 | assert(UI_get_default_method() == ui->method); | |
1656 | UI_set_default_method(UI_OpenSSL()); | |
1657 | UI_destroy_method(ui->method); | |
1658 | return mfree(ui); | |
eac5336c | 1659 | #else |
0bf70b19 DDM |
1660 | assert(ui == NULL); |
1661 | return NULL; | |
eac5336c DDM |
1662 | #endif |
1663 | } | |
1664 | ||
8939d335 DDM |
1665 | int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { |
1666 | #if HAVE_OPENSSL | |
1667 | _cleanup_free_ uint8_t *der = NULL; | |
1668 | int dersz; | |
1669 | ||
1670 | assert(cert); | |
1671 | ||
1672 | dersz = i2d_X509(cert, &der); | |
1673 | if (dersz < 0) | |
60696b22 | 1674 | return log_openssl_errors("Unable to convert PEM certificate to DER format"); |
8939d335 DDM |
1675 | |
1676 | sha256_direct(der, dersz, buffer); | |
1677 | return 0; | |
1678 | #else | |
4e494e6a | 1679 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot calculate X509 fingerprint."); |
8939d335 DDM |
1680 | #endif |
1681 | } | |
a73144bb | 1682 | |
a1d46e30 DDM |
1683 | int openssl_load_x509_certificate( |
1684 | CertificateSourceType certificate_source_type, | |
1685 | const char *certificate_source, | |
1686 | const char *certificate, | |
1687 | X509 **ret) { | |
eac5336c | 1688 | #if HAVE_OPENSSL |
eac5336c DDM |
1689 | int r; |
1690 | ||
a1d46e30 | 1691 | assert(certificate); |
eac5336c | 1692 | |
a1d46e30 | 1693 | switch (certificate_source_type) { |
eac5336c | 1694 | |
a1d46e30 DDM |
1695 | case OPENSSL_CERTIFICATE_SOURCE_FILE: |
1696 | r = load_x509_certificate_from_file(certificate, ret); | |
1697 | break; | |
1698 | case OPENSSL_CERTIFICATE_SOURCE_PROVIDER: | |
1699 | r = load_x509_certificate_from_provider(certificate_source, certificate, ret); | |
1700 | break; | |
1701 | default: | |
1702 | assert_not_reached(); | |
1703 | } | |
1704 | if (r < 0) | |
1705 | return log_debug_errno( | |
1706 | r, | |
1707 | "Failed to load certificate '%s' from OpenSSL certificate source %s: %m", | |
1708 | certificate, | |
1709 | certificate_source); | |
eac5336c DDM |
1710 | |
1711 | return 0; | |
1712 | #else | |
1713 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot load X509 certificate."); | |
1714 | #endif | |
1715 | } | |
1716 | ||
eac5336c DDM |
1717 | int openssl_load_private_key( |
1718 | KeySourceType private_key_source_type, | |
1719 | const char *private_key_source, | |
1720 | const char *private_key, | |
1721 | const AskPasswordRequest *request, | |
1722 | EVP_PKEY **ret_private_key, | |
1723 | OpenSSLAskPasswordUI **ret_user_interface) { | |
0bf70b19 | 1724 | #if HAVE_OPENSSL |
eac5336c DDM |
1725 | int r; |
1726 | ||
1727 | assert(private_key); | |
1728 | assert(request); | |
1729 | ||
1730 | if (private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { | |
1731 | r = openssl_load_private_key_from_file(private_key, ret_private_key); | |
1732 | if (r < 0) | |
1733 | return r; | |
1734 | ||
1735 | *ret_user_interface = NULL; | |
1736 | } else { | |
1737 | _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; | |
0bf70b19 | 1738 | r = openssl_ask_password_ui_new(request, &ui); |
eac5336c DDM |
1739 | if (r < 0) |
1740 | return log_debug_errno(r, "Failed to allocate ask-password user interface: %m"); | |
1741 | ||
0bf70b19 | 1742 | switch (private_key_source_type) { |
eac5336c | 1743 | |
0bf70b19 DDM |
1744 | case OPENSSL_KEY_SOURCE_ENGINE: |
1745 | r = load_key_from_engine(private_key_source, private_key, ret_private_key); | |
1746 | break; | |
1747 | case OPENSSL_KEY_SOURCE_PROVIDER: | |
1748 | r = load_key_from_provider(private_key_source, private_key, ret_private_key); | |
1749 | break; | |
1750 | default: | |
1751 | assert_not_reached(); | |
1752 | } | |
eac5336c DDM |
1753 | if (r < 0) |
1754 | return log_debug_errno( | |
1755 | r, | |
1756 | "Failed to load key '%s' from OpenSSL private key source %s: %m", | |
1757 | private_key, | |
1758 | private_key_source); | |
1759 | ||
1760 | *ret_user_interface = TAKE_PTR(ui); | |
1761 | } | |
1762 | ||
1763 | return 0; | |
0bf70b19 DDM |
1764 | #else |
1765 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot load private key."); | |
1766 | #endif | |
eac5336c DDM |
1767 | } |
1768 | ||
a1d46e30 DDM |
1769 | int parse_openssl_certificate_source_argument( |
1770 | const char *argument, | |
1771 | char **certificate_source, | |
1772 | CertificateSourceType *certificate_source_type) { | |
1773 | ||
1774 | CertificateSourceType type; | |
1775 | const char *e = NULL; | |
1776 | int r; | |
1777 | ||
1778 | assert(argument); | |
1779 | assert(certificate_source); | |
1780 | assert(certificate_source_type); | |
1781 | ||
1782 | if (streq(argument, "file")) | |
1783 | type = OPENSSL_CERTIFICATE_SOURCE_FILE; | |
1784 | else if ((e = startswith(argument, "provider:"))) | |
1785 | type = OPENSSL_CERTIFICATE_SOURCE_PROVIDER; | |
1786 | else | |
1787 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid certificate source '%s'", argument); | |
1788 | ||
1789 | r = free_and_strdup_warn(certificate_source, e); | |
1790 | if (r < 0) | |
1791 | return r; | |
1792 | ||
1793 | *certificate_source_type = type; | |
1794 | ||
1795 | return 0; | |
1796 | } | |
1797 | ||
a73144bb LB |
1798 | int parse_openssl_key_source_argument( |
1799 | const char *argument, | |
1800 | char **private_key_source, | |
1801 | KeySourceType *private_key_source_type) { | |
1802 | ||
1803 | KeySourceType type; | |
1804 | const char *e = NULL; | |
1805 | int r; | |
1806 | ||
1807 | assert(argument); | |
1808 | assert(private_key_source); | |
1809 | assert(private_key_source_type); | |
1810 | ||
1811 | if (streq(argument, "file")) | |
1812 | type = OPENSSL_KEY_SOURCE_FILE; | |
1813 | else if ((e = startswith(argument, "engine:"))) | |
1814 | type = OPENSSL_KEY_SOURCE_ENGINE; | |
1815 | else if ((e = startswith(argument, "provider:"))) | |
1816 | type = OPENSSL_KEY_SOURCE_PROVIDER; | |
1817 | else | |
1818 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid private key source '%s'", argument); | |
1819 | ||
1820 | r = free_and_strdup_warn(private_key_source, e); | |
1821 | if (r < 0) | |
1822 | return r; | |
1823 | ||
1824 | *private_key_source_type = type; | |
1825 | ||
1826 | return 0; | |
1827 | } |