]>
Commit | Line | Data |
---|---|---|
30e31503 ZJS |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
85686b37 VS |
3 | #include <endian.h> |
4 | ||
f2d5df8a | 5 | #include "alloc-util.h" |
a65a25be | 6 | #include "fd-util.h" |
7e8facb3 | 7 | #include "hexdecoct.h" |
85686b37 | 8 | #include "memory-util.h" |
a65a25be | 9 | #include "openssl-util.h" |
876206f2 | 10 | #include "random-util.h" |
a65a25be | 11 | #include "string-util.h" |
f2d5df8a LP |
12 | |
13 | #if HAVE_OPENSSL | |
81d61d6a NL |
14 | # include <openssl/rsa.h> |
15 | # include <openssl/ec.h> | |
16 | ||
17 | # if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) | |
18 | # include <openssl/engine.h> | |
19 | DISABLE_WARNING_DEPRECATED_DECLARATIONS; | |
20 | DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ENGINE*, ENGINE_free, NULL); | |
21 | REENABLE_WARNING; | |
22 | # endif | |
23 | ||
3d05c058 VS |
24 | /* For each error in the OpenSSL thread error queue, log the provided message and the OpenSSL error |
25 | * string. If there are no errors in the OpenSSL thread queue, this logs the message with "No OpenSSL | |
60696b22 DS |
26 | * errors." This logs at level debug. Returns -EIO (or -ENOMEM). */ |
27 | #define log_openssl_errors(fmt, ...) _log_openssl_errors(UNIQ, fmt, ##__VA_ARGS__) | |
28 | #define _log_openssl_errors(u, fmt, ...) \ | |
29 | ({ \ | |
30 | size_t UNIQ_T(MAX, u) = 512 /* arbitrary, but openssl doc states it must be >= 256 */; \ | |
31 | _cleanup_free_ char *UNIQ_T(BUF, u) = malloc(UNIQ_T(MAX, u)); \ | |
32 | !UNIQ_T(BUF, u) \ | |
33 | ? log_oom_debug() \ | |
34 | : __log_openssl_errors(u, UNIQ_T(BUF, u), UNIQ_T(MAX, u), fmt, ##__VA_ARGS__) \ | |
968d232d | 35 | ?: log_debug_errno(SYNTHETIC_ERRNO(EIO), fmt ": No OpenSSL errors.", ##__VA_ARGS__); \ |
60696b22 DS |
36 | }) |
37 | #define __log_openssl_errors(u, buf, max, fmt, ...) \ | |
38 | ({ \ | |
39 | int UNIQ_T(R, u) = 0; \ | |
40 | for (;;) { \ | |
41 | unsigned long UNIQ_T(E, u) = ERR_get_error(); \ | |
42 | if (UNIQ_T(E, u) == 0) \ | |
43 | break; \ | |
44 | ERR_error_string_n(UNIQ_T(E, u), buf, max); \ | |
45 | UNIQ_T(R, u) = log_debug_errno(SYNTHETIC_ERRNO(EIO), fmt ": %s", ##__VA_ARGS__, buf); \ | |
46 | } \ | |
47 | UNIQ_T(R, u); \ | |
48 | }) | |
49 | ||
4af788c7 DS |
50 | int openssl_pkey_from_pem(const void *pem, size_t pem_size, EVP_PKEY **ret) { |
51 | assert(pem); | |
52 | assert(ret); | |
53 | ||
54 | _cleanup_fclose_ FILE *f = NULL; | |
55 | f = fmemopen((void*) pem, pem_size, "r"); | |
56 | if (!f) | |
57 | return log_oom_debug(); | |
58 | ||
59 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL); | |
60 | if (!pkey) | |
60696b22 | 61 | return log_openssl_errors("Failed to parse PEM"); |
4af788c7 DS |
62 | |
63 | *ret = TAKE_PTR(pkey); | |
64 | ||
65 | return 0; | |
66 | } | |
67 | ||
c52a003d DS |
68 | /* Returns the number of bytes generated by the specified digest algorithm. This can be used only for |
69 | * fixed-size algorithms, e.g. md5, sha1, sha256, etc. Do not use this for variable-sized digest algorithms, | |
70 | * e.g. shake128. Returns 0 on success, -EOPNOTSUPP if the algorithm is not supported, or < 0 for any other | |
71 | * error. */ | |
72 | int openssl_digest_size(const char *digest_alg, size_t *ret_digest_size) { | |
73 | assert(digest_alg); | |
74 | assert(ret_digest_size); | |
75 | ||
76 | #if OPENSSL_VERSION_MAJOR >= 3 | |
77 | _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL); | |
78 | #else | |
79 | const EVP_MD *md = EVP_get_digestbyname(digest_alg); | |
80 | #endif | |
81 | if (!md) | |
82 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
83 | "Digest algorithm '%s' not supported.", digest_alg); | |
84 | ||
85 | size_t digest_size; | |
86 | #if OPENSSL_VERSION_MAJOR >= 3 | |
87 | digest_size = EVP_MD_get_size(md); | |
88 | #else | |
89 | digest_size = EVP_MD_size(md); | |
90 | #endif | |
91 | if (digest_size == 0) | |
92 | return log_openssl_errors("Failed to get Digest size"); | |
93 | ||
94 | *ret_digest_size = digest_size; | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
bed4831c DS |
99 | /* Calculate the digest hash value for the provided data, using the specified digest algorithm. Returns 0 on |
100 | * success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any other error. */ | |
101 | int openssl_digest_many( | |
102 | const char *digest_alg, | |
103 | const struct iovec data[], | |
104 | size_t n_data, | |
105 | void **ret_digest, | |
106 | size_t *ret_digest_size) { | |
107 | ||
108 | int r; | |
109 | ||
110 | assert(digest_alg); | |
111 | assert(data || n_data == 0); | |
112 | assert(ret_digest); | |
113 | /* ret_digest_size is optional, as caller may already know the digest size */ | |
114 | ||
115 | #if OPENSSL_VERSION_MAJOR >= 3 | |
116 | _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL); | |
117 | #else | |
118 | const EVP_MD *md = EVP_get_digestbyname(digest_alg); | |
119 | #endif | |
120 | if (!md) | |
121 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
122 | "Digest algorithm '%s' not supported.", digest_alg); | |
123 | ||
124 | _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = EVP_MD_CTX_new(); | |
125 | if (!ctx) | |
126 | return log_openssl_errors("Failed to create new EVP_MD_CTX"); | |
127 | ||
128 | if (!EVP_DigestInit_ex(ctx, md, NULL)) | |
fcdd21ec | 129 | return log_openssl_errors("Failed to initialize EVP_MD_CTX"); |
bed4831c DS |
130 | |
131 | for (size_t i = 0; i < n_data; i++) | |
132 | if (!EVP_DigestUpdate(ctx, data[i].iov_base, data[i].iov_len)) | |
133 | return log_openssl_errors("Failed to update Digest"); | |
134 | ||
135 | size_t digest_size; | |
136 | r = openssl_digest_size(digest_alg, &digest_size); | |
137 | if (r < 0) | |
138 | return r; | |
139 | ||
140 | _cleanup_free_ void *buf = malloc(digest_size); | |
141 | if (!buf) | |
142 | return log_oom_debug(); | |
143 | ||
144 | unsigned int size; | |
145 | if (!EVP_DigestFinal_ex(ctx, buf, &size)) | |
146 | return log_openssl_errors("Failed to finalize Digest"); | |
147 | ||
148 | assert(size == digest_size); | |
149 | ||
150 | *ret_digest = TAKE_PTR(buf); | |
151 | if (ret_digest_size) | |
152 | *ret_digest_size = size; | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
a95e8fa2 DS |
157 | /* Calculate the HMAC digest hash value for the provided data, using the provided key and specified digest |
158 | * algorithm. Returns 0 on success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any | |
159 | * other error. */ | |
160 | int openssl_hmac_many( | |
161 | const char *digest_alg, | |
162 | const void *key, | |
163 | size_t key_size, | |
164 | const struct iovec data[], | |
165 | size_t n_data, | |
166 | void **ret_digest, | |
167 | size_t *ret_digest_size) { | |
168 | ||
169 | assert(digest_alg); | |
170 | assert(key); | |
171 | assert(data || n_data == 0); | |
172 | assert(ret_digest); | |
173 | /* ret_digest_size is optional, as caller may already know the digest size */ | |
174 | ||
175 | #if OPENSSL_VERSION_MAJOR >= 3 | |
176 | _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL); | |
177 | #else | |
178 | const EVP_MD *md = EVP_get_digestbyname(digest_alg); | |
179 | #endif | |
180 | if (!md) | |
181 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
182 | "Digest algorithm '%s' not supported.", digest_alg); | |
183 | ||
184 | #if OPENSSL_VERSION_MAJOR >= 3 | |
185 | _cleanup_(EVP_MAC_freep) EVP_MAC *mac = EVP_MAC_fetch(NULL, "HMAC", NULL); | |
186 | if (!mac) | |
187 | return log_openssl_errors("Failed to create new EVP_MAC"); | |
188 | ||
189 | _cleanup_(EVP_MAC_CTX_freep) EVP_MAC_CTX *ctx = EVP_MAC_CTX_new(mac); | |
190 | if (!ctx) | |
191 | return log_openssl_errors("Failed to create new EVP_MAC_CTX"); | |
192 | ||
193 | _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); | |
194 | if (!bld) | |
195 | return log_openssl_errors("Failed to create new OSSL_PARAM_BLD"); | |
196 | ||
197 | if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_MAC_PARAM_DIGEST, (char*) digest_alg, 0)) | |
198 | return log_openssl_errors("Failed to set HMAC OSSL_MAC_PARAM_DIGEST"); | |
199 | ||
200 | _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); | |
201 | if (!params) | |
202 | return log_openssl_errors("Failed to build HMAC OSSL_PARAM"); | |
203 | ||
204 | if (!EVP_MAC_init(ctx, key, key_size, params)) | |
fcdd21ec | 205 | return log_openssl_errors("Failed to initialize EVP_MAC_CTX"); |
a95e8fa2 DS |
206 | #else |
207 | _cleanup_(HMAC_CTX_freep) HMAC_CTX *ctx = HMAC_CTX_new(); | |
208 | if (!ctx) | |
209 | return log_openssl_errors("Failed to create new HMAC_CTX"); | |
210 | ||
211 | if (!HMAC_Init_ex(ctx, key, key_size, md, NULL)) | |
212 | return log_openssl_errors("Failed to initialize HMAC_CTX"); | |
213 | #endif | |
214 | ||
215 | for (size_t i = 0; i < n_data; i++) | |
216 | #if OPENSSL_VERSION_MAJOR >= 3 | |
217 | if (!EVP_MAC_update(ctx, data[i].iov_base, data[i].iov_len)) | |
218 | #else | |
219 | if (!HMAC_Update(ctx, data[i].iov_base, data[i].iov_len)) | |
220 | #endif | |
221 | return log_openssl_errors("Failed to update HMAC"); | |
222 | ||
223 | size_t digest_size; | |
224 | #if OPENSSL_VERSION_MAJOR >= 3 | |
225 | digest_size = EVP_MAC_CTX_get_mac_size(ctx); | |
226 | #else | |
227 | digest_size = HMAC_size(ctx); | |
228 | #endif | |
229 | if (digest_size == 0) | |
230 | return log_openssl_errors("Failed to get HMAC digest size"); | |
231 | ||
232 | _cleanup_free_ void *buf = malloc(digest_size); | |
233 | if (!buf) | |
234 | return log_oom_debug(); | |
235 | ||
236 | #if OPENSSL_VERSION_MAJOR >= 3 | |
237 | size_t size; | |
238 | if (!EVP_MAC_final(ctx, buf, &size, digest_size)) | |
239 | #else | |
240 | unsigned int size; | |
241 | if (!HMAC_Final(ctx, buf, &size)) | |
242 | #endif | |
243 | return log_openssl_errors("Failed to finalize HMAC"); | |
244 | ||
245 | assert(size == digest_size); | |
246 | ||
247 | *ret_digest = TAKE_PTR(buf); | |
248 | if (ret_digest_size) | |
249 | *ret_digest_size = size; | |
250 | ||
251 | return 0; | |
252 | } | |
253 | ||
58f215a0 DS |
254 | /* Symmetric Cipher encryption using the alg-bits-mode cipher, e.g. AES-128-CFB. The key is required and must |
255 | * be at least the minimum required key length for the cipher. The IV is optional but, if provided, it must | |
256 | * be at least the minimum iv length for the cipher. If no IV is provided and the cipher requires one, a | |
257 | * buffer of zeroes is used. Returns 0 on success, -EOPNOTSUPP if the cipher algorithm is not supported, or < | |
258 | * 0 on any other error. */ | |
259 | int openssl_cipher_many( | |
260 | const char *alg, | |
261 | size_t bits, | |
262 | const char *mode, | |
263 | const void *key, | |
264 | size_t key_size, | |
265 | const void *iv, | |
266 | size_t iv_size, | |
267 | const struct iovec data[], | |
268 | size_t n_data, | |
269 | void **ret, | |
270 | size_t *ret_size) { | |
271 | ||
272 | assert(alg); | |
273 | assert(bits > 0); | |
274 | assert(mode); | |
275 | assert(key); | |
276 | assert(iv || iv_size == 0); | |
277 | assert(data || n_data == 0); | |
278 | assert(ret); | |
279 | assert(ret_size); | |
280 | ||
281 | _cleanup_free_ char *cipher_alg = NULL; | |
282 | if (asprintf(&cipher_alg, "%s-%zu-%s", alg, bits, mode) < 0) | |
283 | return log_oom_debug(); | |
284 | ||
285 | #if OPENSSL_VERSION_MAJOR >= 3 | |
286 | _cleanup_(EVP_CIPHER_freep) EVP_CIPHER *cipher = EVP_CIPHER_fetch(NULL, cipher_alg, NULL); | |
287 | #else | |
288 | const EVP_CIPHER *cipher = EVP_get_cipherbyname(cipher_alg); | |
289 | #endif | |
290 | if (!cipher) | |
291 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
292 | "Cipher algorithm '%s' not supported.", cipher_alg); | |
293 | ||
294 | _cleanup_(EVP_CIPHER_CTX_freep) EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new(); | |
295 | if (!ctx) | |
296 | return log_openssl_errors("Failed to create new EVP_CIPHER_CTX"); | |
297 | ||
298 | /* Verify enough key data was provided. */ | |
299 | int cipher_key_length = EVP_CIPHER_key_length(cipher); | |
300 | assert(cipher_key_length >= 0); | |
301 | if ((size_t) cipher_key_length > key_size) | |
302 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
303 | "Not enough key bytes provided, require %d", cipher_key_length); | |
304 | ||
305 | /* Verify enough IV data was provided or, if no IV was provided, use a zeroed buffer for IV data. */ | |
306 | int cipher_iv_length = EVP_CIPHER_iv_length(cipher); | |
307 | assert(cipher_iv_length >= 0); | |
308 | _cleanup_free_ void *zero_iv = NULL; | |
309 | if (iv_size == 0) { | |
310 | zero_iv = malloc0(cipher_iv_length); | |
311 | if (!zero_iv) | |
312 | return log_oom_debug(); | |
313 | ||
314 | iv = zero_iv; | |
315 | iv_size = (size_t) cipher_iv_length; | |
316 | } | |
317 | if ((size_t) cipher_iv_length > iv_size) | |
318 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), | |
319 | "Not enough IV bytes provided, require %d", cipher_iv_length); | |
320 | ||
321 | if (!EVP_EncryptInit(ctx, cipher, key, iv)) | |
322 | return log_openssl_errors("Failed to initialize EVP_CIPHER_CTX."); | |
323 | ||
324 | int cipher_block_size = EVP_CIPHER_CTX_block_size(ctx); | |
325 | assert(cipher_block_size > 0); | |
326 | ||
327 | _cleanup_free_ uint8_t *buf = NULL; | |
328 | size_t size = 0; | |
329 | ||
330 | for (size_t i = 0; i < n_data; i++) { | |
331 | /* Cipher may produce (up to) input length + cipher block size of output. */ | |
332 | if (!GREEDY_REALLOC(buf, size + data[i].iov_len + cipher_block_size)) | |
333 | return log_oom_debug(); | |
334 | ||
335 | int update_size; | |
336 | if (!EVP_EncryptUpdate(ctx, &buf[size], &update_size, data[i].iov_base, data[i].iov_len)) | |
337 | return log_openssl_errors("Failed to update Cipher."); | |
338 | ||
339 | size += update_size; | |
340 | } | |
341 | ||
342 | if (!GREEDY_REALLOC(buf, size + cipher_block_size)) | |
343 | return log_oom_debug(); | |
344 | ||
345 | int final_size; | |
346 | if (!EVP_EncryptFinal_ex(ctx, &buf[size], &final_size)) | |
347 | return log_openssl_errors("Failed to finalize Cipher."); | |
348 | ||
349 | *ret = TAKE_PTR(buf); | |
350 | *ret_size = size + final_size; | |
351 | ||
352 | return 0; | |
353 | } | |
354 | ||
8c2205bb DS |
355 | /* Perform Single-Step (aka "Concat") KDF. Currently, this only supports using the digest for the auxiliary |
356 | * function. The derive_size parameter specifies how many bytes are derived. | |
357 | * | |
358 | * For more details see: https://www.openssl.org/docs/manmaster/man7/EVP_KDF-SS.html */ | |
359 | int kdf_ss_derive( | |
360 | const char *digest, | |
361 | const void *key, | |
362 | size_t key_size, | |
363 | const void *salt, | |
364 | size_t salt_size, | |
365 | const void *info, | |
366 | size_t info_size, | |
367 | size_t derive_size, | |
368 | void **ret) { | |
369 | ||
370 | #if OPENSSL_VERSION_MAJOR >= 3 | |
371 | assert(digest); | |
372 | assert(key); | |
373 | assert(derive_size > 0); | |
374 | assert(ret); | |
375 | ||
376 | _cleanup_(EVP_KDF_freep) EVP_KDF *kdf = EVP_KDF_fetch(NULL, "SSKDF", NULL); | |
377 | if (!kdf) | |
378 | return log_openssl_errors("Failed to create new EVP_KDF"); | |
379 | ||
380 | _cleanup_(EVP_KDF_CTX_freep) EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf); | |
381 | if (!ctx) | |
382 | return log_openssl_errors("Failed to create new EVP_KDF_CTX"); | |
383 | ||
384 | _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); | |
385 | if (!bld) | |
386 | return log_openssl_errors("Failed to create new OSSL_PARAM_BLD"); | |
387 | ||
388 | _cleanup_free_ void *buf = malloc(derive_size); | |
389 | if (!buf) | |
390 | return log_oom_debug(); | |
391 | ||
392 | if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_DIGEST, (char*) digest, 0)) | |
393 | return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_DIGEST"); | |
394 | ||
395 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_KEY, (char*) key, key_size)) | |
396 | return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_KEY"); | |
397 | ||
398 | if (salt) | |
399 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SALT, (char*) salt, salt_size)) | |
400 | return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_SALT"); | |
401 | ||
402 | if (info) | |
403 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_INFO, (char*) info, info_size)) | |
404 | return log_openssl_errors("Failed to add KDF-SS OSSL_KDF_PARAM_INFO"); | |
405 | ||
406 | _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); | |
407 | if (!params) | |
408 | return log_openssl_errors("Failed to build KDF-SS OSSL_PARAM"); | |
409 | ||
410 | if (EVP_KDF_derive(ctx, buf, derive_size, params) <= 0) | |
968d232d | 411 | return log_openssl_errors("OpenSSL KDF-SS derive failed"); |
8c2205bb DS |
412 | |
413 | *ret = TAKE_PTR(buf); | |
414 | ||
415 | return 0; | |
416 | #else | |
968d232d | 417 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "KDF-SS requires OpenSSL >= 3."); |
8c2205bb DS |
418 | #endif |
419 | } | |
420 | ||
a65a25be | 421 | /* Perform Key-Based HMAC KDF. The mode must be "COUNTER" or "FEEDBACK". The parameter naming is from the |
968d232d | 422 | * OpenSSL api, and maps to SP800-108 naming as "...key, salt, info, and seed correspond to KI, Label, |
a65a25be DS |
423 | * Context, and IV (respectively)...". The derive_size parameter specifies how many bytes are derived. |
424 | * | |
425 | * For more details see: https://www.openssl.org/docs/manmaster/man7/EVP_KDF-KB.html */ | |
426 | int kdf_kb_hmac_derive( | |
427 | const char *mode, | |
428 | const char *digest, | |
429 | const void *key, | |
430 | size_t key_size, | |
431 | const void *salt, | |
432 | size_t salt_size, | |
433 | const void *info, | |
434 | size_t info_size, | |
435 | const void *seed, | |
436 | size_t seed_size, | |
437 | size_t derive_size, | |
438 | void **ret) { | |
439 | ||
440 | #if OPENSSL_VERSION_MAJOR >= 3 | |
441 | assert(mode); | |
442 | assert(strcaseeq(mode, "COUNTER") || strcaseeq(mode, "FEEDBACK")); | |
443 | assert(digest); | |
444 | assert(key || key_size == 0); | |
445 | assert(salt || salt_size == 0); | |
446 | assert(info || info_size == 0); | |
447 | assert(seed || seed_size == 0); | |
448 | assert(derive_size > 0); | |
449 | assert(ret); | |
450 | ||
451 | _cleanup_(EVP_KDF_freep) EVP_KDF *kdf = EVP_KDF_fetch(NULL, "KBKDF", NULL); | |
452 | if (!kdf) | |
453 | return log_openssl_errors("Failed to create new EVP_KDF"); | |
454 | ||
455 | _cleanup_(EVP_KDF_CTX_freep) EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf); | |
456 | if (!ctx) | |
457 | return log_openssl_errors("Failed to create new EVP_KDF_CTX"); | |
458 | ||
459 | _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); | |
460 | if (!bld) | |
461 | return log_openssl_errors("Failed to create new OSSL_PARAM_BLD"); | |
462 | ||
463 | if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_MAC, (char*) "HMAC", 0)) | |
464 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_MAC"); | |
465 | ||
466 | if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_MODE, (char*) mode, 0)) | |
467 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_MODE"); | |
468 | ||
469 | if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_KDF_PARAM_DIGEST, (char*) digest, 0)) | |
470 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_DIGEST"); | |
471 | ||
472 | if (key) | |
473 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_KEY, (char*) key, key_size)) | |
474 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_KEY"); | |
475 | ||
476 | if (salt) | |
477 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SALT, (char*) salt, salt_size)) | |
478 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_SALT"); | |
479 | ||
480 | if (info) | |
481 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_INFO, (char*) info, info_size)) | |
482 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_INFO"); | |
483 | ||
484 | if (seed) | |
485 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_KDF_PARAM_SEED, (char*) seed, seed_size)) | |
486 | return log_openssl_errors("Failed to add KDF-KB OSSL_KDF_PARAM_SEED"); | |
487 | ||
488 | _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); | |
489 | if (!params) | |
490 | return log_openssl_errors("Failed to build KDF-KB OSSL_PARAM"); | |
491 | ||
492 | _cleanup_free_ void *buf = malloc(derive_size); | |
493 | if (!buf) | |
494 | return log_oom_debug(); | |
495 | ||
496 | if (EVP_KDF_derive(ctx, buf, derive_size, params) <= 0) | |
968d232d | 497 | return log_openssl_errors("OpenSSL KDF-KB derive failed"); |
a65a25be DS |
498 | |
499 | *ret = TAKE_PTR(buf); | |
500 | ||
501 | return 0; | |
502 | #else | |
968d232d | 503 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "KDF-KB requires OpenSSL >= 3."); |
a65a25be DS |
504 | #endif |
505 | } | |
506 | ||
f2d5df8a LP |
507 | int rsa_encrypt_bytes( |
508 | EVP_PKEY *pkey, | |
509 | const void *decrypted_key, | |
510 | size_t decrypted_key_size, | |
511 | void **ret_encrypt_key, | |
512 | size_t *ret_encrypt_key_size) { | |
513 | ||
514 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL; | |
515 | _cleanup_free_ void *b = NULL; | |
516 | size_t l; | |
517 | ||
518 | ctx = EVP_PKEY_CTX_new(pkey, NULL); | |
519 | if (!ctx) | |
60696b22 | 520 | return log_openssl_errors("Failed to allocate public key context"); |
f2d5df8a LP |
521 | |
522 | if (EVP_PKEY_encrypt_init(ctx) <= 0) | |
60696b22 | 523 | return log_openssl_errors("Failed to initialize public key context"); |
f2d5df8a LP |
524 | |
525 | if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) | |
60696b22 | 526 | return log_openssl_errors("Failed to configure PKCS#1 padding"); |
f2d5df8a LP |
527 | |
528 | if (EVP_PKEY_encrypt(ctx, NULL, &l, decrypted_key, decrypted_key_size) <= 0) | |
60696b22 | 529 | return log_openssl_errors("Failed to determine encrypted key size"); |
f2d5df8a LP |
530 | |
531 | b = malloc(l); | |
532 | if (!b) | |
533 | return -ENOMEM; | |
534 | ||
535 | if (EVP_PKEY_encrypt(ctx, b, &l, decrypted_key, decrypted_key_size) <= 0) | |
60696b22 | 536 | return log_openssl_errors("Failed to determine encrypted key size"); |
f2d5df8a LP |
537 | |
538 | *ret_encrypt_key = TAKE_PTR(b); | |
539 | *ret_encrypt_key_size = l; | |
f2d5df8a LP |
540 | return 0; |
541 | } | |
d041e4fc | 542 | |
816b1dc4 DS |
543 | /* Encrypt the key data using RSA-OAEP with the provided label and specified digest algorithm. Returns 0 on |
544 | * success, -EOPNOTSUPP if the digest algorithm is not supported, or < 0 for any other error. */ | |
545 | int rsa_oaep_encrypt_bytes( | |
546 | const EVP_PKEY *pkey, | |
547 | const char *digest_alg, | |
548 | const char *label, | |
549 | const void *decrypted_key, | |
550 | size_t decrypted_key_size, | |
551 | void **ret_encrypt_key, | |
552 | size_t *ret_encrypt_key_size) { | |
553 | ||
554 | assert(pkey); | |
555 | assert(digest_alg); | |
556 | assert(label); | |
557 | assert(decrypted_key); | |
558 | assert(decrypted_key_size > 0); | |
559 | assert(ret_encrypt_key); | |
560 | assert(ret_encrypt_key_size); | |
561 | ||
562 | #if OPENSSL_VERSION_MAJOR >= 3 | |
563 | _cleanup_(EVP_MD_freep) EVP_MD *md = EVP_MD_fetch(NULL, digest_alg, NULL); | |
564 | #else | |
565 | const EVP_MD *md = EVP_get_digestbyname(digest_alg); | |
566 | #endif | |
567 | if (!md) | |
568 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
569 | "Digest algorithm '%s' not supported.", digest_alg); | |
570 | ||
571 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) pkey, NULL); | |
572 | if (!ctx) | |
573 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); | |
574 | ||
575 | if (EVP_PKEY_encrypt_init(ctx) <= 0) | |
576 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); | |
577 | ||
578 | if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) | |
579 | return log_openssl_errors("Failed to configure RSA-OAEP padding"); | |
580 | ||
581 | if (EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) <= 0) | |
582 | return log_openssl_errors("Failed to configure RSA-OAEP MD"); | |
583 | ||
584 | _cleanup_free_ char *duplabel = strdup(label); | |
585 | if (!duplabel) | |
586 | return log_oom_debug(); | |
587 | ||
588 | if (EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, duplabel, strlen(duplabel) + 1) <= 0) | |
589 | return log_openssl_errors("Failed to configure RSA-OAEP label"); | |
590 | /* ctx owns this now, don't free */ | |
591 | TAKE_PTR(duplabel); | |
592 | ||
593 | size_t size = 0; | |
594 | if (EVP_PKEY_encrypt(ctx, NULL, &size, decrypted_key, decrypted_key_size) <= 0) | |
595 | return log_openssl_errors("Failed to determine RSA-OAEP encrypted key size"); | |
596 | ||
597 | _cleanup_free_ void *buf = malloc(size); | |
598 | if (!buf) | |
599 | return log_oom_debug(); | |
600 | ||
601 | if (EVP_PKEY_encrypt(ctx, buf, &size, decrypted_key, decrypted_key_size) <= 0) | |
602 | return log_openssl_errors("Failed to RSA-OAEP encrypt"); | |
603 | ||
604 | *ret_encrypt_key = TAKE_PTR(buf); | |
605 | *ret_encrypt_key_size = size; | |
606 | ||
607 | return 0; | |
608 | } | |
609 | ||
d041e4fc LP |
610 | int rsa_pkey_to_suitable_key_size( |
611 | EVP_PKEY *pkey, | |
612 | size_t *ret_suitable_key_size) { | |
613 | ||
614 | size_t suitable_key_size; | |
d041e4fc LP |
615 | int bits; |
616 | ||
2e64df07 YW |
617 | assert(pkey); |
618 | assert(ret_suitable_key_size); | |
d041e4fc LP |
619 | |
620 | /* Analyzes the specified public key and that it is RSA. If so, will return a suitable size for a | |
621 | * disk encryption key to encrypt with RSA for use in PKCS#11 security token schemes. */ | |
622 | ||
623 | if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) | |
624 | return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "X.509 certificate does not refer to RSA key."); | |
625 | ||
7f12adc3 | 626 | bits = EVP_PKEY_bits(pkey); |
d041e4fc LP |
627 | log_debug("Bits in RSA key: %i", bits); |
628 | ||
629 | /* We use PKCS#1 padding for the RSA cleartext, hence let's leave some extra space for it, hence only | |
630 | * generate a random key half the size of the RSA length */ | |
631 | suitable_key_size = bits / 8 / 2; | |
632 | ||
633 | if (suitable_key_size < 1) | |
634 | return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Uh, RSA key size too short?"); | |
635 | ||
636 | *ret_suitable_key_size = suitable_key_size; | |
637 | return 0; | |
638 | } | |
7e8facb3 | 639 | |
85686b37 VS |
640 | /* Generate RSA public key from provided "n" and "e" values. Numbers "n" and "e" must be provided here |
641 | * in big-endian format, e.g. wrap it with htobe32() for uint32_t. */ | |
dcec950c DS |
642 | int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size, EVP_PKEY **ret) { |
643 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; | |
644 | ||
645 | assert(n); | |
85686b37 | 646 | assert(n_size != 0); |
dcec950c | 647 | assert(e); |
85686b37 | 648 | assert(e_size != 0); |
dcec950c DS |
649 | assert(ret); |
650 | ||
85686b37 VS |
651 | #if OPENSSL_VERSION_MAJOR >= 3 |
652 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); | |
dcec950c | 653 | if (!ctx) |
60696b22 | 654 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); |
dcec950c | 655 | |
dcec950c | 656 | if (EVP_PKEY_fromdata_init(ctx) <= 0) |
60696b22 | 657 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); |
dcec950c | 658 | |
85686b37 | 659 | OSSL_PARAM params[3]; |
dcec950c | 660 | |
85686b37 VS |
661 | #if __BYTE_ORDER == __BIG_ENDIAN |
662 | params[0] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, (void*)n, n_size); | |
663 | params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, (void*)e, e_size); | |
664 | #else | |
665 | _cleanup_free_ void *native_n = memdup_reverse(n, n_size); | |
666 | if (!native_n) | |
667 | return log_oom_debug(); | |
dcec950c | 668 | |
85686b37 VS |
669 | _cleanup_free_ void *native_e = memdup_reverse(e, e_size); |
670 | if (!native_e) | |
671 | return log_oom_debug(); | |
dcec950c | 672 | |
85686b37 VS |
673 | params[0] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, native_n, n_size); |
674 | params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, native_e, e_size); | |
675 | #endif | |
676 | params[2] = OSSL_PARAM_construct_end(); | |
dcec950c DS |
677 | |
678 | if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) | |
60696b22 | 679 | return log_openssl_errors("Failed to create RSA EVP_PKEY"); |
dcec950c | 680 | #else |
85686b37 VS |
681 | _cleanup_(BN_freep) BIGNUM *bn_n = BN_bin2bn(n, n_size, NULL); |
682 | if (!bn_n) | |
683 | return log_openssl_errors("Failed to create BIGNUM for RSA n"); | |
684 | ||
685 | _cleanup_(BN_freep) BIGNUM *bn_e = BN_bin2bn(e, e_size, NULL); | |
686 | if (!bn_e) | |
687 | return log_openssl_errors("Failed to create BIGNUM for RSA e"); | |
688 | ||
dcec950c DS |
689 | _cleanup_(RSA_freep) RSA *rsa_key = RSA_new(); |
690 | if (!rsa_key) | |
60696b22 | 691 | return log_openssl_errors("Failed to create new RSA"); |
dcec950c DS |
692 | |
693 | if (!RSA_set0_key(rsa_key, bn_n, bn_e, NULL)) | |
60696b22 | 694 | return log_openssl_errors("Failed to set RSA n/e"); |
dcec950c DS |
695 | /* rsa_key owns these now, don't free */ |
696 | TAKE_PTR(bn_n); | |
697 | TAKE_PTR(bn_e); | |
698 | ||
699 | pkey = EVP_PKEY_new(); | |
700 | if (!pkey) | |
60696b22 | 701 | return log_openssl_errors("Failed to create new EVP_PKEY"); |
dcec950c DS |
702 | |
703 | if (!EVP_PKEY_assign_RSA(pkey, rsa_key)) | |
60696b22 | 704 | return log_openssl_errors("Failed to assign RSA key"); |
dcec950c DS |
705 | /* pkey owns this now, don't free */ |
706 | TAKE_PTR(rsa_key); | |
707 | #endif | |
708 | ||
709 | *ret = TAKE_PTR(pkey); | |
710 | ||
711 | return 0; | |
712 | } | |
713 | ||
714 | /* Get the "n" and "e" values from the pkey. The values are returned in "bin" format, i.e. BN_bn2bin(). */ | |
715 | int rsa_pkey_to_n_e( | |
716 | const EVP_PKEY *pkey, | |
717 | void **ret_n, | |
718 | size_t *ret_n_size, | |
719 | void **ret_e, | |
720 | size_t *ret_e_size) { | |
721 | ||
722 | assert(pkey); | |
723 | assert(ret_n); | |
724 | assert(ret_n_size); | |
725 | assert(ret_e); | |
726 | assert(ret_e_size); | |
727 | ||
728 | #if OPENSSL_VERSION_MAJOR >= 3 | |
729 | _cleanup_(BN_freep) BIGNUM *bn_n = NULL; | |
730 | if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &bn_n)) | |
60696b22 | 731 | return log_openssl_errors("Failed to get RSA n"); |
dcec950c DS |
732 | |
733 | _cleanup_(BN_freep) BIGNUM *bn_e = NULL; | |
734 | if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &bn_e)) | |
60696b22 | 735 | return log_openssl_errors("Failed to get RSA e"); |
dcec950c DS |
736 | #else |
737 | const RSA *rsa = EVP_PKEY_get0_RSA((EVP_PKEY*) pkey); | |
738 | if (!rsa) | |
60696b22 | 739 | return log_openssl_errors("Failed to get RSA key from public key"); |
dcec950c DS |
740 | |
741 | const BIGNUM *bn_n = RSA_get0_n(rsa); | |
742 | if (!bn_n) | |
60696b22 | 743 | return log_openssl_errors("Failed to get RSA n"); |
dcec950c DS |
744 | |
745 | const BIGNUM *bn_e = RSA_get0_e(rsa); | |
746 | if (!bn_e) | |
60696b22 | 747 | return log_openssl_errors("Failed to get RSA e"); |
dcec950c DS |
748 | #endif |
749 | ||
750 | size_t n_size = BN_num_bytes(bn_n), e_size = BN_num_bytes(bn_e); | |
751 | _cleanup_free_ void *n = malloc(n_size), *e = malloc(e_size); | |
752 | if (!n || !e) | |
753 | return log_oom_debug(); | |
754 | ||
755 | assert(BN_bn2bin(bn_n, n) == (int) n_size); | |
756 | assert(BN_bn2bin(bn_e, e) == (int) e_size); | |
757 | ||
758 | *ret_n = TAKE_PTR(n); | |
759 | *ret_n_size = n_size; | |
760 | *ret_e = TAKE_PTR(e); | |
761 | *ret_e_size = e_size; | |
762 | ||
763 | return 0; | |
764 | } | |
765 | ||
766 | /* Generate a new RSA key with the specified number of bits. */ | |
767 | int rsa_pkey_new(size_t bits, EVP_PKEY **ret) { | |
768 | assert(ret); | |
769 | ||
770 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); | |
771 | if (!ctx) | |
60696b22 | 772 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); |
dcec950c DS |
773 | |
774 | if (EVP_PKEY_keygen_init(ctx) <= 0) | |
60696b22 | 775 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); |
dcec950c DS |
776 | |
777 | if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, (int) bits) <= 0) | |
60696b22 | 778 | return log_openssl_errors("Failed to set RSA bits to %zu", bits); |
dcec950c DS |
779 | |
780 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; | |
781 | if (EVP_PKEY_keygen(ctx, &pkey) <= 0) | |
60696b22 | 782 | return log_openssl_errors("Failed to generate ECC key"); |
dcec950c DS |
783 | |
784 | *ret = TAKE_PTR(pkey); | |
785 | ||
786 | return 0; | |
787 | } | |
788 | ||
900e73f8 DS |
789 | /* Generate ECC public key from provided curve ID and x/y points. */ |
790 | int ecc_pkey_from_curve_x_y( | |
791 | int curve_id, | |
792 | const void *x, | |
793 | size_t x_size, | |
794 | const void *y, | |
795 | size_t y_size, | |
796 | EVP_PKEY **ret) { | |
797 | ||
798 | assert(x); | |
799 | assert(y); | |
800 | assert(ret); | |
801 | ||
802 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); | |
803 | if (!ctx) | |
60696b22 | 804 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); |
900e73f8 | 805 | |
60696b22 DS |
806 | _cleanup_(BN_freep) BIGNUM *bn_x = BN_bin2bn(x, x_size, NULL); |
807 | if (!bn_x) | |
808 | return log_openssl_errors("Failed to create BIGNUM x"); | |
809 | ||
810 | _cleanup_(BN_freep) BIGNUM *bn_y = BN_bin2bn(y, y_size, NULL); | |
811 | if (!bn_y) | |
812 | return log_openssl_errors("Failed to create BIGNUM y"); | |
900e73f8 DS |
813 | |
814 | _cleanup_(EC_GROUP_freep) EC_GROUP *group = EC_GROUP_new_by_curve_name(curve_id); | |
815 | if (!group) | |
60696b22 | 816 | return log_openssl_errors("ECC curve id %d not supported", curve_id); |
900e73f8 DS |
817 | |
818 | _cleanup_(EC_POINT_freep) EC_POINT *point = EC_POINT_new(group); | |
819 | if (!point) | |
60696b22 | 820 | return log_openssl_errors("Failed to create new EC_POINT"); |
900e73f8 DS |
821 | |
822 | if (!EC_POINT_set_affine_coordinates(group, point, bn_x, bn_y, NULL)) | |
60696b22 | 823 | return log_openssl_errors("Failed to set ECC coordinates"); |
900e73f8 DS |
824 | |
825 | #if OPENSSL_VERSION_MAJOR >= 3 | |
826 | if (EVP_PKEY_fromdata_init(ctx) <= 0) | |
60696b22 | 827 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); |
900e73f8 DS |
828 | |
829 | _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); | |
830 | if (!bld) | |
60696b22 | 831 | return log_openssl_errors("Failed to create new OSSL_PARAM_BLD"); |
900e73f8 DS |
832 | |
833 | if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, (char*) OSSL_EC_curve_nid2name(curve_id), 0)) | |
60696b22 | 834 | return log_openssl_errors("Failed to add ECC OSSL_PKEY_PARAM_GROUP_NAME"); |
900e73f8 DS |
835 | |
836 | _cleanup_(OPENSSL_freep) void *pbuf = NULL; | |
837 | size_t pbuf_len = 0; | |
838 | pbuf_len = EC_POINT_point2buf(group, point, POINT_CONVERSION_UNCOMPRESSED, (unsigned char**) &pbuf, NULL); | |
839 | if (pbuf_len == 0) | |
60696b22 | 840 | return log_openssl_errors("Failed to convert ECC point to buffer"); |
900e73f8 DS |
841 | |
842 | if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, pbuf, pbuf_len)) | |
60696b22 | 843 | return log_openssl_errors("Failed to add ECC OSSL_PKEY_PARAM_PUB_KEY"); |
900e73f8 DS |
844 | |
845 | _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(bld); | |
846 | if (!params) | |
60696b22 | 847 | return log_openssl_errors("Failed to build ECC OSSL_PARAM"); |
900e73f8 DS |
848 | |
849 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; | |
850 | if (EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) | |
60696b22 | 851 | return log_openssl_errors("Failed to create ECC EVP_PKEY"); |
900e73f8 DS |
852 | #else |
853 | _cleanup_(EC_KEY_freep) EC_KEY *eckey = EC_KEY_new(); | |
854 | if (!eckey) | |
60696b22 | 855 | return log_openssl_errors("Failed to create new EC_KEY"); |
900e73f8 DS |
856 | |
857 | if (!EC_KEY_set_group(eckey, group)) | |
60696b22 | 858 | return log_openssl_errors("Failed to set ECC group"); |
900e73f8 DS |
859 | |
860 | if (!EC_KEY_set_public_key(eckey, point)) | |
60696b22 | 861 | return log_openssl_errors("Failed to set ECC point"); |
900e73f8 DS |
862 | |
863 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = EVP_PKEY_new(); | |
864 | if (!pkey) | |
60696b22 | 865 | return log_openssl_errors("Failed to create new EVP_PKEY"); |
900e73f8 DS |
866 | |
867 | if (!EVP_PKEY_assign_EC_KEY(pkey, eckey)) | |
60696b22 | 868 | return log_openssl_errors("Failed to assign ECC key"); |
900e73f8 DS |
869 | /* pkey owns this now, don't free */ |
870 | TAKE_PTR(eckey); | |
871 | #endif | |
872 | ||
873 | *ret = TAKE_PTR(pkey); | |
874 | ||
875 | return 0; | |
876 | } | |
877 | ||
878 | int ecc_pkey_to_curve_x_y( | |
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; | |
887 | int curve_id; | |
888 | ||
889 | assert(pkey); | |
890 | ||
891 | #if OPENSSL_VERSION_MAJOR >= 3 | |
892 | size_t name_size; | |
893 | if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0, &name_size)) | |
60696b22 | 894 | return log_openssl_errors("Failed to get ECC group name size"); |
900e73f8 | 895 | |
b0307102 | 896 | _cleanup_free_ char *name = new(char, name_size + 1); |
900e73f8 DS |
897 | if (!name) |
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)) | |
60696b22 | 901 | return log_openssl_errors("Failed to get ECC group name"); |
900e73f8 DS |
902 | |
903 | curve_id = OBJ_sn2nid(name); | |
904 | if (curve_id == NID_undef) | |
60696b22 | 905 | return log_openssl_errors("Failed to get ECC curve id"); |
900e73f8 DS |
906 | |
907 | if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_X, &bn_x)) | |
60696b22 | 908 | return log_openssl_errors("Failed to get ECC point x"); |
900e73f8 DS |
909 | |
910 | if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_EC_PUB_Y, &bn_y)) | |
60696b22 | 911 | return log_openssl_errors("Failed to get ECC point y"); |
900e73f8 DS |
912 | #else |
913 | const EC_KEY *eckey = EVP_PKEY_get0_EC_KEY((EVP_PKEY*) pkey); | |
914 | if (!eckey) | |
60696b22 | 915 | return log_openssl_errors("Failed to get EC_KEY"); |
900e73f8 DS |
916 | |
917 | const EC_GROUP *group = EC_KEY_get0_group(eckey); | |
918 | if (!group) | |
60696b22 | 919 | return log_openssl_errors("Failed to get EC_GROUP"); |
900e73f8 DS |
920 | |
921 | curve_id = EC_GROUP_get_curve_name(group); | |
922 | if (curve_id == NID_undef) | |
60696b22 | 923 | return log_openssl_errors("Failed to get ECC curve id"); |
900e73f8 DS |
924 | |
925 | const EC_POINT *point = EC_KEY_get0_public_key(eckey); | |
926 | if (!point) | |
60696b22 | 927 | return log_openssl_errors("Failed to get EC_POINT"); |
900e73f8 DS |
928 | |
929 | bn_x = BN_new(); | |
930 | bn_y = BN_new(); | |
931 | if (!bn_x || !bn_y) | |
60696b22 | 932 | return log_openssl_errors("Failed to create new BIGNUM"); |
900e73f8 DS |
933 | |
934 | if (!EC_POINT_get_affine_coordinates(group, point, bn_x, bn_y, NULL)) | |
60696b22 | 935 | return log_openssl_errors("Failed to get ECC x/y."); |
900e73f8 DS |
936 | #endif |
937 | ||
938 | size_t x_size = BN_num_bytes(bn_x), y_size = BN_num_bytes(bn_y); | |
939 | _cleanup_free_ void *x = malloc(x_size), *y = malloc(y_size); | |
940 | if (!x || !y) | |
941 | return log_oom_debug(); | |
942 | ||
943 | assert(BN_bn2bin(bn_x, x) == (int) x_size); | |
944 | assert(BN_bn2bin(bn_y, y) == (int) y_size); | |
945 | ||
946 | if (ret_curve_id) | |
947 | *ret_curve_id = curve_id; | |
948 | if (ret_x) | |
949 | *ret_x = TAKE_PTR(x); | |
950 | if (ret_x_size) | |
951 | *ret_x_size = x_size; | |
952 | if (ret_y) | |
953 | *ret_y = TAKE_PTR(y); | |
954 | if (ret_y_size) | |
955 | *ret_y_size = y_size; | |
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) { | |
962 | assert(ret); | |
963 | ||
964 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); | |
965 | if (!ctx) | |
60696b22 | 966 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); |
900e73f8 DS |
967 | |
968 | if (EVP_PKEY_keygen_init(ctx) <= 0) | |
60696b22 | 969 | return log_openssl_errors("Failed to initialize EVP_PKEY_CTX"); |
900e73f8 DS |
970 | |
971 | if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curve_id) <= 0) | |
60696b22 | 972 | return log_openssl_errors("Failed to set ECC curve %d", curve_id); |
900e73f8 DS |
973 | |
974 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; | |
975 | if (EVP_PKEY_keygen(ctx, &pkey) <= 0) | |
60696b22 | 976 | return log_openssl_errors("Failed to generate ECC key"); |
900e73f8 DS |
977 | |
978 | *ret = TAKE_PTR(pkey); | |
979 | ||
980 | return 0; | |
981 | } | |
982 | ||
779b80d8 DS |
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, | |
989 | const EVP_PKEY *peer_pkey, | |
990 | void **ret_shared_secret, | |
991 | size_t *ret_shared_secret_size) { | |
992 | ||
993 | assert(private_pkey); | |
994 | assert(peer_pkey); | |
995 | assert(ret_shared_secret); | |
996 | assert(ret_shared_secret_size); | |
997 | ||
998 | _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new((EVP_PKEY*) private_pkey, NULL); | |
999 | if (!ctx) | |
1000 | return log_openssl_errors("Failed to create new EVP_PKEY_CTX"); | |
1001 | ||
1002 | if (EVP_PKEY_derive_init(ctx) <= 0) | |
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) | |
1006 | return log_openssl_errors("Failed to set ECC derive peer"); | |
1007 | ||
1008 | size_t shared_secret_size; | |
1009 | if (EVP_PKEY_derive(ctx, NULL, &shared_secret_size) <= 0) | |
1010 | return log_openssl_errors("Failed to get ECC shared secret size"); | |
1011 | ||
3d05c058 | 1012 | _cleanup_(erase_and_freep) void *shared_secret = malloc(shared_secret_size); |
779b80d8 DS |
1013 | if (!shared_secret) |
1014 | return log_oom_debug(); | |
1015 | ||
1016 | if (EVP_PKEY_derive(ctx, (unsigned char*) shared_secret, &shared_secret_size) <= 0) | |
1017 | return log_openssl_errors("Failed to derive ECC shared secret"); | |
1018 | ||
1019 | *ret_shared_secret = TAKE_PTR(shared_secret); | |
1020 | *ret_shared_secret_size = shared_secret_size; | |
1021 | ||
1022 | return 0; | |
1023 | } | |
1024 | ||
e8ccb5c7 LP |
1025 | int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) { |
1026 | _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL; | |
1027 | _cleanup_free_ void *d = NULL, *h = NULL; | |
1028 | int sz, lsz, msz; | |
1029 | unsigned umsz; | |
1030 | unsigned char *dd; | |
1031 | ||
1032 | /* Calculates a message digest of the DER encoded public key */ | |
1033 | ||
1034 | assert(pk); | |
1035 | assert(md); | |
1036 | assert(ret); | |
1037 | assert(ret_size); | |
1038 | ||
1039 | sz = i2d_PublicKey(pk, NULL); | |
1040 | if (sz < 0) | |
60696b22 | 1041 | return log_openssl_errors("Unable to convert public key to DER format"); |
e8ccb5c7 LP |
1042 | |
1043 | dd = d = malloc(sz); | |
1044 | if (!d) | |
1045 | return log_oom_debug(); | |
1046 | ||
1047 | lsz = i2d_PublicKey(pk, &dd); | |
1048 | if (lsz < 0) | |
60696b22 | 1049 | return log_openssl_errors("Unable to convert public key to DER format"); |
e8ccb5c7 LP |
1050 | |
1051 | m = EVP_MD_CTX_new(); | |
1052 | if (!m) | |
60696b22 | 1053 | return log_openssl_errors("Failed to create new EVP_MD_CTX"); |
e8ccb5c7 LP |
1054 | |
1055 | if (EVP_DigestInit_ex(m, md, NULL) != 1) | |
60696b22 | 1056 | return log_openssl_errors("Failed to initialize %s context", EVP_MD_name(md)); |
e8ccb5c7 LP |
1057 | |
1058 | if (EVP_DigestUpdate(m, d, lsz) != 1) | |
60696b22 | 1059 | return log_openssl_errors("Failed to run %s context", EVP_MD_name(md)); |
e8ccb5c7 LP |
1060 | |
1061 | msz = EVP_MD_size(md); | |
2e64df07 | 1062 | assert(msz > 0); |
e8ccb5c7 LP |
1063 | |
1064 | h = malloc(msz); | |
1065 | if (!h) | |
1066 | return log_oom_debug(); | |
1067 | ||
1068 | umsz = msz; | |
1069 | if (EVP_DigestFinal_ex(m, h, &umsz) != 1) | |
60696b22 | 1070 | return log_openssl_errors("Failed to finalize hash context"); |
e8ccb5c7 | 1071 | |
2e64df07 | 1072 | assert(umsz == (unsigned) msz); |
e8ccb5c7 LP |
1073 | |
1074 | *ret = TAKE_PTR(h); | |
1075 | *ret_size = msz; | |
1076 | ||
1077 | return 0; | |
1078 | } | |
1079 | ||
ef65c0f6 LP |
1080 | int digest_and_sign( |
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 | assert(privkey); | |
1087 | assert(ret); | |
1088 | assert(ret_size); | |
1089 | ||
1090 | if (size == 0) | |
1091 | data = ""; /* make sure to pass a valid pointer to OpenSSL */ | |
1092 | else { | |
1093 | assert(data); | |
1094 | ||
1095 | if (size == SIZE_MAX) /* If SIZE_MAX input is a string whose size we determine automatically */ | |
1096 | size = strlen(data); | |
1097 | } | |
1098 | ||
1099 | _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = EVP_MD_CTX_new(); | |
1100 | if (!mdctx) | |
1101 | return log_openssl_errors("Failed to create new EVP_MD_CTX"); | |
1102 | ||
1103 | if (EVP_DigestSignInit(mdctx, NULL, md, NULL, privkey) != 1) | |
1104 | return log_openssl_errors("Failed to initialize signature context"); | |
1105 | ||
1106 | /* Determine signature size */ | |
1107 | size_t ss; | |
1108 | if (EVP_DigestSign(mdctx, NULL, &ss, data, size) != 1) | |
1109 | return log_openssl_errors("Failed to determine size of signature"); | |
1110 | ||
1111 | _cleanup_free_ void *sig = malloc(ss); | |
1112 | if (!sig) | |
1113 | return log_oom_debug(); | |
1114 | ||
1115 | if (EVP_DigestSign(mdctx, sig, &ss, data, size) != 1) | |
1116 | return log_openssl_errors("Failed to sign data"); | |
1117 | ||
1118 | *ret = TAKE_PTR(sig); | |
1119 | *ret_size = ss; | |
1120 | return 0; | |
1121 | } | |
1122 | ||
7e8facb3 ZJS |
1123 | # if PREFER_OPENSSL |
1124 | int string_hashsum( | |
1125 | const char *s, | |
1126 | size_t len, | |
11f7bc5e | 1127 | const char *md_algorithm, |
7e8facb3 ZJS |
1128 | char **ret) { |
1129 | ||
11f7bc5e | 1130 | _cleanup_free_ void *hash = NULL; |
7e8facb3 | 1131 | size_t hash_size; |
38e1035b | 1132 | _cleanup_free_ char *enc = NULL; |
7e8facb3 ZJS |
1133 | int r; |
1134 | ||
11f7bc5e DS |
1135 | assert(s || len == 0); |
1136 | assert(md_algorithm); | |
1137 | assert(ret); | |
7e8facb3 | 1138 | |
11f7bc5e | 1139 | r = openssl_digest(md_algorithm, s, len, &hash, &hash_size); |
7e8facb3 ZJS |
1140 | if (r < 0) |
1141 | return r; | |
1142 | ||
1143 | enc = hexmem(hash, hash_size); | |
1144 | if (!enc) | |
1145 | return -ENOMEM; | |
1146 | ||
11f7bc5e | 1147 | *ret = TAKE_PTR(enc); |
7e8facb3 | 1148 | return 0; |
7e8facb3 ZJS |
1149 | } |
1150 | # endif | |
876206f2 | 1151 | |
3d05c058 VS |
1152 | static int ecc_pkey_generate_volume_keys( |
1153 | EVP_PKEY *pkey, | |
1154 | void **ret_decrypted_key, | |
1155 | size_t *ret_decrypted_key_size, | |
1156 | void **ret_saved_key, | |
1157 | size_t *ret_saved_key_size) { | |
1158 | ||
1159 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_new = NULL; | |
1160 | _cleanup_(erase_and_freep) void *decrypted_key = NULL; | |
1161 | _cleanup_free_ unsigned char *saved_key = NULL; | |
1162 | size_t decrypted_key_size, saved_key_size; | |
1163 | int nid = NID_undef; | |
1164 | int r; | |
1165 | ||
1166 | #if OPENSSL_VERSION_MAJOR >= 3 | |
1167 | _cleanup_free_ char *curve_name = NULL; | |
1168 | size_t len = 0; | |
1169 | ||
1170 | if (EVP_PKEY_get_group_name(pkey, NULL, 0, &len) != 1 || len == 0) | |
1171 | return log_openssl_errors("Failed to determine PKEY group name length"); | |
1172 | ||
1173 | len++; | |
1174 | curve_name = new(char, len); | |
1175 | if (!curve_name) | |
1176 | return log_oom_debug(); | |
1177 | ||
1178 | if (EVP_PKEY_get_group_name(pkey, curve_name, len, &len) != 1) | |
1179 | return log_openssl_errors("Failed to get PKEY group name"); | |
1180 | ||
1181 | nid = OBJ_sn2nid(curve_name); | |
1182 | #else | |
1183 | EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); | |
1184 | if (!ec_key) | |
1185 | return log_openssl_errors("PKEY doesn't have EC_KEY associated"); | |
1186 | ||
1187 | if (EC_KEY_check_key(ec_key) != 1) | |
1188 | return log_openssl_errors("EC_KEY associated with PKEY is not valid"); | |
1189 | ||
1190 | nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); | |
1191 | #endif | |
1192 | ||
1193 | r = ecc_pkey_new(nid, &pkey_new); | |
1194 | if (r < 0) | |
1195 | return log_debug_errno(r, "Failed to generate a new EC keypair: %m"); | |
1196 | ||
1197 | r = ecc_ecdh(pkey_new, pkey, &decrypted_key, &decrypted_key_size); | |
1198 | if (r < 0) | |
1199 | return log_debug_errno(r, "Failed to derive shared secret: %m"); | |
1200 | ||
1201 | #if OPENSSL_VERSION_MAJOR >= 3 | |
1202 | /* EVP_PKEY_get1_encoded_public_key() always returns uncompressed format of EC points. | |
1203 | See https://github.com/openssl/openssl/discussions/22835 */ | |
1204 | saved_key_size = EVP_PKEY_get1_encoded_public_key(pkey_new, &saved_key); | |
1205 | if (saved_key_size == 0) | |
1206 | return log_openssl_errors("Failed to convert the generated public key to SEC1 format"); | |
1207 | #else | |
1208 | EC_KEY *ec_key_new = EVP_PKEY_get0_EC_KEY(pkey_new); | |
1209 | if (!ec_key_new) | |
1210 | return log_openssl_errors("The generated key doesn't have associated EC_KEY"); | |
1211 | ||
1212 | if (EC_KEY_check_key(ec_key_new) != 1) | |
1213 | return log_openssl_errors("EC_KEY associated with the generated key is not valid"); | |
1214 | ||
1215 | saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new), | |
1216 | EC_KEY_get0_public_key(ec_key_new), | |
1217 | POINT_CONVERSION_UNCOMPRESSED, | |
1218 | NULL, 0, NULL); | |
1219 | if (saved_key_size == 0) | |
1220 | return log_openssl_errors("Failed to determine size of the generated public key"); | |
1221 | ||
1222 | saved_key = malloc(saved_key_size); | |
1223 | if (!saved_key) | |
1224 | return log_oom_debug(); | |
1225 | ||
1226 | saved_key_size = EC_POINT_point2oct(EC_KEY_get0_group(ec_key_new), | |
1227 | EC_KEY_get0_public_key(ec_key_new), | |
1228 | POINT_CONVERSION_UNCOMPRESSED, | |
1229 | saved_key, saved_key_size, NULL); | |
1230 | if (saved_key_size == 0) | |
1231 | return log_openssl_errors("Failed to convert the generated public key to SEC1 format"); | |
1232 | #endif | |
1233 | ||
1234 | *ret_decrypted_key = TAKE_PTR(decrypted_key); | |
1235 | *ret_decrypted_key_size = decrypted_key_size; | |
1236 | *ret_saved_key = TAKE_PTR(saved_key); | |
1237 | *ret_saved_key_size = saved_key_size; | |
1238 | return 0; | |
1239 | } | |
1240 | ||
876206f2 VS |
1241 | static int rsa_pkey_generate_volume_keys( |
1242 | EVP_PKEY *pkey, | |
1243 | void **ret_decrypted_key, | |
1244 | size_t *ret_decrypted_key_size, | |
1245 | void **ret_saved_key, | |
1246 | size_t *ret_saved_key_size) { | |
1247 | ||
1248 | _cleanup_(erase_and_freep) void *decrypted_key = NULL; | |
1249 | _cleanup_free_ void *saved_key = NULL; | |
1250 | size_t decrypted_key_size, saved_key_size; | |
1251 | int r; | |
1252 | ||
1253 | r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size); | |
1254 | if (r < 0) | |
1255 | return log_debug_errno(r, "Failed to determine RSA public key size."); | |
1256 | ||
1257 | log_debug("Generating %zu bytes random key.", decrypted_key_size); | |
1258 | ||
1259 | decrypted_key = malloc(decrypted_key_size); | |
1260 | if (!decrypted_key) | |
1261 | return log_oom_debug(); | |
1262 | ||
1263 | r = crypto_random_bytes(decrypted_key, decrypted_key_size); | |
1264 | if (r < 0) | |
1265 | return log_debug_errno(r, "Failed to generate random key: %m"); | |
1266 | ||
1267 | r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &saved_key, &saved_key_size); | |
1268 | if (r < 0) | |
1269 | return log_debug_errno(r, "Failed to encrypt random key: %m"); | |
1270 | ||
1271 | *ret_decrypted_key = TAKE_PTR(decrypted_key); | |
1272 | *ret_decrypted_key_size = decrypted_key_size; | |
1273 | *ret_saved_key = TAKE_PTR(saved_key); | |
1274 | *ret_saved_key_size = saved_key_size; | |
1275 | return 0; | |
1276 | } | |
1277 | ||
85686b37 VS |
1278 | int pkey_generate_volume_keys( |
1279 | EVP_PKEY *pkey, | |
876206f2 VS |
1280 | void **ret_decrypted_key, |
1281 | size_t *ret_decrypted_key_size, | |
1282 | void **ret_saved_key, | |
1283 | size_t *ret_saved_key_size) { | |
1284 | ||
85686b37 | 1285 | assert(pkey); |
876206f2 VS |
1286 | assert(ret_decrypted_key); |
1287 | assert(ret_decrypted_key_size); | |
1288 | assert(ret_saved_key); | |
1289 | assert(ret_saved_key_size); | |
1290 | ||
876206f2 VS |
1291 | #if OPENSSL_VERSION_MAJOR >= 3 |
1292 | int type = EVP_PKEY_get_base_id(pkey); | |
1293 | #else | |
1294 | int type = EVP_PKEY_base_id(pkey); | |
1295 | #endif | |
1296 | switch (type) { | |
1297 | ||
1298 | case EVP_PKEY_RSA: | |
1299 | return rsa_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size); | |
1300 | ||
3d05c058 VS |
1301 | case EVP_PKEY_EC: |
1302 | return ecc_pkey_generate_volume_keys(pkey, ret_decrypted_key, ret_decrypted_key_size, ret_saved_key, ret_saved_key_size); | |
1303 | ||
876206f2 | 1304 | case NID_undef: |
4e494e6a | 1305 | return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine a type of public key."); |
876206f2 VS |
1306 | |
1307 | default: | |
1308 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported public key type: %s", OBJ_nid2sn(type)); | |
1309 | } | |
1310 | } | |
dba0afa1 LB |
1311 | |
1312 | static int load_key_from_provider(const char *provider, const char *private_key_uri, EVP_PKEY **ret) { | |
1313 | ||
1314 | assert(provider); | |
1315 | assert(private_key_uri); | |
1316 | assert(ret); | |
1317 | ||
1318 | #if OPENSSL_VERSION_MAJOR >= 3 | |
1319 | /* Load the provider so that this can work without any custom written configuration in /etc/. | |
1320 | * Also load the 'default' as that seems to be the recommendation. */ | |
1321 | if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true)) | |
1322 | return log_openssl_errors("Failed to load OpenSSL provider '%s'", provider); | |
1323 | if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true)) | |
1324 | return log_openssl_errors("Failed to load OpenSSL provider 'default'"); | |
1325 | ||
1326 | _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open( | |
1327 | private_key_uri, | |
1328 | /* ui_method= */ NULL, | |
1329 | /* ui_data= */ NULL, | |
1330 | /* post_process= */ NULL, | |
1331 | /* post_process_data= */ NULL); | |
1332 | if (!store) | |
1333 | return log_openssl_errors("Failed to open OpenSSL store via '%s'", private_key_uri); | |
1334 | ||
1335 | _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store); | |
1336 | if (!info) | |
1337 | return log_openssl_errors("Failed to load OpenSSL store via '%s'", private_key_uri); | |
1338 | ||
1339 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = OSSL_STORE_INFO_get1_PKEY(info); | |
1340 | if (!private_key) | |
1341 | return log_openssl_errors("Failed to load private key via '%s'", private_key_uri); | |
1342 | ||
1343 | *ret = TAKE_PTR(private_key); | |
1344 | ||
1345 | return 0; | |
1346 | #else | |
1347 | return -EOPNOTSUPP; | |
1348 | #endif | |
1349 | } | |
1350 | ||
1351 | static int load_key_from_engine(const char *engine, const char *private_key_uri, EVP_PKEY **ret) { | |
1352 | ||
1353 | assert(engine); | |
1354 | assert(private_key_uri); | |
1355 | assert(ret); | |
1356 | ||
81d61d6a | 1357 | #if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) |
dba0afa1 LB |
1358 | DISABLE_WARNING_DEPRECATED_DECLARATIONS; |
1359 | _cleanup_(ENGINE_freep) ENGINE *e = ENGINE_by_id(engine); | |
1360 | if (!e) | |
1361 | return log_openssl_errors("Failed to load signing engine '%s'", engine); | |
1362 | ||
1363 | if (ENGINE_init(e) == 0) | |
1364 | return log_openssl_errors("Failed to initialize signing engine '%s'", engine); | |
1365 | ||
1366 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = ENGINE_load_private_key( | |
1367 | e, | |
1368 | private_key_uri, | |
1369 | /* ui_method= */ NULL, | |
1370 | /* callback_data= */ NULL); | |
1371 | if (!private_key) | |
1372 | return log_openssl_errors("Failed to load private key from '%s'", private_key_uri); | |
1373 | REENABLE_WARNING; | |
1374 | ||
1375 | *ret = TAKE_PTR(private_key); | |
1376 | ||
1377 | return 0; | |
81d61d6a NL |
1378 | #else |
1379 | return -EOPNOTSUPP; | |
1380 | #endif | |
dba0afa1 LB |
1381 | } |
1382 | ||
a73144bb LB |
1383 | int openssl_load_key_from_token( |
1384 | KeySourceType private_key_source_type, | |
1385 | const char *private_key_source, | |
1386 | const char *private_key, | |
1387 | EVP_PKEY **ret) { | |
dba0afa1 | 1388 | |
a73144bb LB |
1389 | assert(IN_SET(private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER)); |
1390 | assert(private_key_source); | |
1391 | assert(private_key); | |
dba0afa1 | 1392 | |
a73144bb | 1393 | switch (private_key_source_type) { |
dba0afa1 | 1394 | |
a73144bb LB |
1395 | case OPENSSL_KEY_SOURCE_ENGINE: |
1396 | return load_key_from_engine(private_key_source, private_key, ret); | |
1397 | case OPENSSL_KEY_SOURCE_PROVIDER: | |
1398 | return load_key_from_provider(private_key_source, private_key, ret); | |
1399 | default: | |
1400 | assert_not_reached(); | |
dba0afa1 | 1401 | } |
dba0afa1 | 1402 | } |
f2d5df8a | 1403 | #endif |
8939d335 DDM |
1404 | |
1405 | int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { | |
1406 | #if HAVE_OPENSSL | |
1407 | _cleanup_free_ uint8_t *der = NULL; | |
1408 | int dersz; | |
1409 | ||
1410 | assert(cert); | |
1411 | ||
1412 | dersz = i2d_X509(cert, &der); | |
1413 | if (dersz < 0) | |
60696b22 | 1414 | return log_openssl_errors("Unable to convert PEM certificate to DER format"); |
8939d335 DDM |
1415 | |
1416 | sha256_direct(der, dersz, buffer); | |
1417 | return 0; | |
1418 | #else | |
4e494e6a | 1419 | return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot calculate X509 fingerprint."); |
8939d335 DDM |
1420 | #endif |
1421 | } | |
a73144bb LB |
1422 | |
1423 | int parse_openssl_key_source_argument( | |
1424 | const char *argument, | |
1425 | char **private_key_source, | |
1426 | KeySourceType *private_key_source_type) { | |
1427 | ||
1428 | KeySourceType type; | |
1429 | const char *e = NULL; | |
1430 | int r; | |
1431 | ||
1432 | assert(argument); | |
1433 | assert(private_key_source); | |
1434 | assert(private_key_source_type); | |
1435 | ||
1436 | if (streq(argument, "file")) | |
1437 | type = OPENSSL_KEY_SOURCE_FILE; | |
1438 | else if ((e = startswith(argument, "engine:"))) | |
1439 | type = OPENSSL_KEY_SOURCE_ENGINE; | |
1440 | else if ((e = startswith(argument, "provider:"))) | |
1441 | type = OPENSSL_KEY_SOURCE_PROVIDER; | |
1442 | else | |
1443 | return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid private key source '%s'", argument); | |
1444 | ||
1445 | r = free_and_strdup_warn(private_key_source, e); | |
1446 | if (r < 0) | |
1447 | return r; | |
1448 | ||
1449 | *private_key_source_type = type; | |
1450 | ||
1451 | return 0; | |
1452 | } |