From 7e7fb27dd82a9bb3aee63f80560678b47246527a Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Fri, 31 May 2024 12:48:25 -0400 Subject: [PATCH 1/2] Add support for legacy RSA_PKCS1_WITH_TLS_PADDING Fixes #375 Signed-off-by: Simo Sorce --- src/asymmetric_cipher.c | 208 +++++++++++++++++++++++++++++++++++----- src/util.h | 12 +++ 2 files changed, 198 insertions(+), 22 deletions(-) diff --git a/src/asymmetric_cipher.c b/src/asymmetric_cipher.c index 294076b6..4d87b1cf 100644 --- a/src/asymmetric_cipher.c +++ b/src/asymmetric_cipher.c @@ -3,7 +3,9 @@ #include "provider.h" #include +#include "openssl/prov_ssl.h" #include "openssl/rsa.h" +#include "openssl/rand.h" DISPATCH_RSAENC_FN(newctx); DISPATCH_RSAENC_FN(freectx); @@ -17,6 +19,14 @@ DISPATCH_RSAENC_FN(set_ctx_params); DISPATCH_RSAENC_FN(gettable_ctx_params); DISPATCH_RSAENC_FN(settable_ctx_params); +struct tls_padding { + bool enabled; + CK_BYTE client_ver_major; + CK_BYTE client_ver_minor; + CK_BYTE alt_ver_major; + CK_BYTE alt_ver_minor; +}; + struct p11prov_rsaenc_ctx { P11PROV_CTX *provctx; @@ -24,6 +34,9 @@ struct p11prov_rsaenc_ctx { CK_MECHANISM_TYPE mechtype; CK_RSA_PKCS_OAEP_PARAMS oaep_params; + + /* RSA_PKCS1_WITH_TLS_PADDING parameters */ + struct tls_padding tls_padding; }; static void *p11prov_rsaenc_newctx(void *provctx) @@ -85,6 +98,7 @@ static void *p11prov_rsaenc_dupctx(void *ctx) } dst->ulSourceDataLen = src->ulSourceDataLen; } + newctx->tls_padding = encctx->tls_padding; return newctx; } @@ -233,49 +247,128 @@ static int p11prov_rsaenc_decrypt_init(void *ctx, void *provkey, return p11prov_rsaenc_set_ctx_params(ctx, params); } +/* we are guaranteed to have enough space in buf for our checks */ +static int +p11prov_tls_constant_time_depadding(struct p11prov_rsaenc_ctx *encctx, + unsigned char *out, unsigned char *buf, + size_t *out_size, CK_ULONG *ret_cond) +{ + unsigned char randbuf[SSL_MAX_MASTER_KEY_LENGTH]; + CK_ULONG ver_cond = 0; + CK_ULONG cond = 0; + size_t length = SSL_MAX_MASTER_KEY_LENGTH; + int err; + + /* always generate a random buffer, to constant_time swap in + * in case any of the tests fails */ + err = RAND_priv_bytes_ex(p11prov_ctx_get_libctx(encctx->provctx), randbuf, + sizeof(randbuf), 0); + if (err != RET_OSSL_OK) { + /* it is ok to bail out here because the error is completely + * unrelated to the computations and is not a controllable + * side-channel */ + ERR_raise(ERR_LIB_RSA, ERR_R_INTERNAL_ERROR); + return RET_OSSL_ERR; + } + + cond = constant_equal(*out_size, 2 + length); + + ver_cond = constant_equal(buf[0], encctx->tls_padding.client_ver_major); + ver_cond &= constant_equal(buf[1], encctx->tls_padding.client_ver_minor); + + /* this conditional is ok because it does not depend on the outcome of + * the decryption or any other private data */ + if (encctx->tls_padding.alt_ver_major != 0) { + CK_ULONG alt_cond = 0; + alt_cond = constant_equal(buf[0], encctx->tls_padding.alt_ver_major); + alt_cond &= constant_equal(buf[1], encctx->tls_padding.alt_ver_minor); + ver_cond |= alt_cond; + } + cond &= ver_cond; + + constant_select_buf(cond, length, out, buf + 2, randbuf); + + *out_size = length; + *ret_cond = cond; + return RET_OSSL_OK; +} + static int p11prov_rsaenc_decrypt(void *ctx, unsigned char *out, size_t *outlen, size_t outsize, const unsigned char *in, size_t inlen) { struct p11prov_rsaenc_ctx *encctx = (struct p11prov_rsaenc_ctx *)ctx; CK_MECHANISM mechanism; - P11PROV_SESSION *session; + P11PROV_SESSION *session = NULL; CK_SESSION_HANDLE sess; CK_SLOT_ID slotid; CK_OBJECT_HANDLE handle; - CK_ULONG out_size = *outlen; + CK_ULONG key_size = CK_UNAVAILABLE_INFORMATION; + unsigned char *tmpbuf = NULL; + unsigned char *outbuf = out; + CK_ULONG out_size = outsize; int result = RET_OSSL_ERR; bool always_auth = false; + bool tls_padding = encctx->tls_padding.enabled; CK_RV ret; P11PROV_debug("decrypt (ctx=%p)", ctx); + key_size = p11prov_obj_get_key_size(encctx->key); + if (key_size == CK_UNAVAILABLE_INFORMATION) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return RET_OSSL_ERR; + } + if (out == NULL) { - CK_ULONG size = p11prov_obj_get_key_size(encctx->key); - if (size == CK_UNAVAILABLE_INFORMATION) { - ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); - return RET_OSSL_ERR; + if (encctx->tls_padding.enabled) { + *outlen = SSL_MAX_MASTER_KEY_LENGTH; + } else { + *outlen = key_size; } - *outlen = size; return RET_OSSL_OK; } + if (outsize < key_size) { + if (tls_padding) { + if (outsize < SSL_MAX_MASTER_KEY_LENGTH) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return RET_OSSL_ERR; + } + } else { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return RET_OSSL_ERR; + } + } + + if (tls_padding) { + tmpbuf = OPENSSL_zalloc(key_size); + if (!tmpbuf) { + return RET_OSSL_ERR; + } + outbuf = tmpbuf; + out_size = key_size; + } + slotid = p11prov_obj_get_slotid(encctx->key); if (slotid == CK_UNAVAILABLE_INFORMATION) { P11PROV_raise(encctx->provctx, CKR_SLOT_ID_INVALID, "Provided key has invalid slot"); - return RET_OSSL_ERR; + result = RET_OSSL_ERR; + goto done; } handle = p11prov_obj_get_handle(encctx->key); if (handle == CK_INVALID_HANDLE) { P11PROV_raise(encctx->provctx, CKR_KEY_HANDLE_INVALID, "Provided key has invalid handle"); - return RET_OSSL_ERR; + result = RET_OSSL_ERR; + goto done; } ret = p11prov_rsaenc_set_mechanism(encctx, &mechanism); if (ret != CKR_OK) { - return RET_OSSL_ERR; + result = RET_OSSL_ERR; + goto done; } ret = p11prov_get_session(encctx->provctx, &slotid, NULL, NULL, @@ -284,7 +377,8 @@ static int p11prov_rsaenc_decrypt(void *ctx, unsigned char *out, size_t *outlen, if (ret != CKR_OK) { P11PROV_raise(encctx->provctx, ret, "Failed to open session on slot %lu", slotid); - return RET_OSSL_ERR; + result = RET_OSSL_ERR; + goto done; } sess = p11prov_session_handle(session); @@ -294,7 +388,8 @@ static int p11prov_rsaenc_decrypt(void *ctx, unsigned char *out, size_t *outlen, || ret == CKR_MECHANISM_PARAM_INVALID) { ERR_raise(ERR_LIB_PROV, PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE); } - goto endsess; + result = RET_OSSL_ERR; + goto done; } always_auth = @@ -302,7 +397,8 @@ static int p11prov_rsaenc_decrypt(void *ctx, unsigned char *out, size_t *outlen, if (always_auth) { ret = p11prov_context_specific_login(session, NULL, NULL, NULL); if (ret != CKR_OK) { - goto endsess; + result = RET_OSSL_ERR; + goto done; } } @@ -310,27 +406,45 @@ static int p11prov_rsaenc_decrypt(void *ctx, unsigned char *out, size_t *outlen, if (mechanism.mechanism == CKM_RSA_PKCS) { CK_ULONG cond; ret = side_channel_free_Decrypt(encctx->provctx, sess, (void *)in, - inlen, out, &out_size); + inlen, outbuf, &out_size); /* the error case need to be handled in a side-channel free way, so * conditionals need to be constant time. Always setting outlen is * fine because out_size is initialized to the value of outlen * and the value should not matter in an error condition anyway */ - *outlen = out_size; cond = constant_equal(ret, CKR_OK); + + /* this conditional is ok because it is not dependent on the + * decryption computation or any private data */ + if (tls_padding) { + CK_ULONG tls_cond = 0; + + result = p11prov_tls_constant_time_depadding(encctx, out, tmpbuf, + &out_size, &tls_cond); + /* this conditional is ok because only fatal failures + * unrelated to the computation are returned this way. + * Like allocation failures or rng failures. */ + if (result != RET_OSSL_OK) { + goto done; + } + cond &= tls_cond; + } result = constant_select_int(cond, RET_OSSL_OK, RET_OSSL_ERR); - goto endsess; + *outlen = out_size; + goto done; } ret = p11prov_Decrypt(encctx->provctx, sess, (void *)in, inlen, out, &out_size); if (ret != CKR_OK) { - goto endsess; + result = RET_OSSL_ERR; + goto done; } *outlen = out_size; result = RET_OSSL_OK; -endsess: +done: p11prov_return_session(session); + OPENSSL_clear_free(tmpbuf, key_size); return result; } @@ -341,6 +455,8 @@ static struct { } padding_map[] = { { CKM_RSA_X_509, RSA_NO_PADDING, OSSL_PKEY_RSA_PAD_MODE_NONE }, { CKM_RSA_PKCS, RSA_PKCS1_PADDING, OSSL_PKEY_RSA_PAD_MODE_PKCSV15 }, + { CKM_RSA_PKCS, RSA_PKCS1_WITH_TLS_PADDING, + OSSL_PKEY_RSA_PAD_MODE_PKCSV15 }, { CKM_RSA_PKCS_OAEP, RSA_PKCS1_OAEP_PADDING, OSSL_PKEY_RSA_PAD_MODE_OAEP }, { CKM_RSA_X9_31, RSA_X931_PADDING, OSSL_PKEY_RSA_PAD_MODE_X931 }, { CK_UNAVAILABLE_INFORMATION, 0, NULL }, @@ -464,6 +580,27 @@ static int p11prov_rsaenc_get_ctx_params(void *ctx, OSSL_PARAM *params) } } + p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION); + if (p) { + ret = OSSL_PARAM_set_uint( + p, (((unsigned int)encctx->tls_padding.client_ver_major) << 8) + + encctx->tls_padding.client_ver_minor); + if (ret != RET_OSSL_OK) { + return ret; + } + } + + p = OSSL_PARAM_locate(params, + OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION); + if (p) { + ret = OSSL_PARAM_set_uint( + p, (((unsigned int)encctx->tls_padding.alt_ver_major) << 8) + + encctx->tls_padding.alt_ver_minor); + if (ret != RET_OSSL_OK) { + return ret; + } + } + return RET_OSSL_OK; } @@ -495,6 +632,9 @@ static int p11prov_rsaenc_set_ctx_params(void *ctx, const OSSL_PARAM params[]) break; } } + if (pad_mode == RSA_PKCS1_WITH_TLS_PADDING) { + encctx->tls_padding.enabled = true; + } } else if (p->data_type == OSSL_PARAM_UTF8_STRING) { if (p->data) { for (int i = 0; padding_map[i].string != NULL; i++) { @@ -567,6 +707,34 @@ static int p11prov_rsaenc_set_ctx_params(void *ctx, const OSSL_PARAM params[]) encctx->oaep_params.ulSourceDataLen = len; } + p = OSSL_PARAM_locate_const(params, + OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION); + if (p) { + unsigned int client_ver; + + ret = OSSL_PARAM_get_uint(p, &client_ver); + if (ret != RET_OSSL_OK) { + return ret; + } + + encctx->tls_padding.client_ver_major = (client_ver >> 8) & 0xff; + encctx->tls_padding.client_ver_minor = client_ver & 0xff; + } + + p = OSSL_PARAM_locate_const(params, + OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION); + if (p) { + unsigned int alt_ver; + + ret = OSSL_PARAM_get_uint(p, &alt_ver); + if (ret != RET_OSSL_OK) { + return ret; + } + + encctx->tls_padding.alt_ver_major = (alt_ver >> 8) & 0xff; + encctx->tls_padding.alt_ver_minor = alt_ver & 0xff; + } + return RET_OSSL_OK; } @@ -578,10 +746,8 @@ static const OSSL_PARAM *p11prov_rsaenc_gettable_ctx_params(void *ctx, OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, NULL, 0), OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, NULL, 0), OSSL_PARAM_octet_string(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, NULL, 0), - /* OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL), OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL), - */ OSSL_PARAM_END, }; return params; @@ -597,10 +763,8 @@ static const OSSL_PARAM *p11prov_rsaenc_settable_ctx_params(void *ctx, OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS, NULL, 0), OSSL_PARAM_octet_string(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, NULL, 0), - /* OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL), OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL), - */ OSSL_PARAM_END, }; return params; diff --git a/src/util.h b/src/util.h index f316c61a..bcbc2db5 100644 --- a/src/util.h +++ b/src/util.h @@ -120,4 +120,16 @@ static inline int constant_select_int(CK_ULONG cond, int a, int b) return (int)((A & mask) | (B & ~mask)); } +static inline void constant_select_buf(CK_ULONG cond, size_t size, + unsigned char *dst, unsigned char *a, + unsigned char *b) +{ + for (int i = 0; i < size; i++) { + volatile unsigned char A = a[i]; + volatile unsigned char B = b[i]; + volatile unsigned char mask = -(unsigned char)cond; + dst[i] = ((A & mask) | (B & ~mask)); + } +} + #endif /* _UTIL_H */ From 554730b01b5e5687a869387d1a5b0a2fd55aa880 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 3 Jun 2024 14:23:58 -0400 Subject: [PATCH 2/2] Add test to verify TLS depadding works correctly Signed-off-by: Simo Sorce --- tests/meson.build | 24 +++++----- tests/tcmpkeys.c | 81 +------------------------------- tests/tlsctx.c | 114 ++++++++++++++++++++++++++++++++++++++-------- tests/util.c | 94 ++++++++++++++++++++++++++++++++++++++ tests/util.h | 5 ++ 5 files changed, 206 insertions(+), 112 deletions(-) create mode 100644 tests/util.c create mode 100644 tests/util.h diff --git a/tests/meson.build b/tests/meson.build index 07c53607..2dcb1e5a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -93,20 +93,20 @@ if get_option('b_sanitize') == 'address' endif endif -test_programs = [ - 'tsession', - 'tgenkey', - 'tlsctx', - 'tdigests', - 'treadkeys', - 'tcmpkeys', - 'tfork', - 'pincache', -] +test_programs = { + 'tsession': ['tsession.c'], + 'tgenkey': ['tgenkey.c'], + 'tlsctx': ['tlsctx.c', 'util.c'], + 'tdigests': ['tdigests.c'], + 'treadkeys': ['treadkeys.c'], + 'tcmpkeys': ['tcmpkeys.c', 'util.c'], + 'tfork': ['tfork.c'], + 'pincache': ['pincache.c'], +} test_executables = [] -foreach t : test_programs - t = executable(t, '@0@.c'.format(t), +foreach t, sources : test_programs + t = executable(t, sources, build_by_default: false, include_directories: [configinc], dependencies: [libcrypto, libssl]) diff --git a/tests/tcmpkeys.c b/tests/tcmpkeys.c index b261729c..8402c0cb 100644 --- a/tests/tcmpkeys.c +++ b/tests/tcmpkeys.c @@ -7,86 +7,7 @@ #include #include #include - -static void ossl_err_print(void) -{ - bool first = true; - unsigned long err = 0; - while (true) { - const char *file, *func, *data; - int line; - err = ERR_get_error_all(&file, &line, &func, &data, NULL); - if (err == 0) { - break; - } - - char buf[1024]; - ERR_error_string_n(err, buf, sizeof(buf)); - - const char *fmt = - first ? ": %s (in function %s in %s:%d): %s\n" - : " caused by: %s (in function %s in %s:%d): %s\n"; - fprintf(stderr, fmt, buf, func, file, line, data); - - first = false; - } - if (first) { - fprintf(stderr, "\n"); - } -} - -static EVP_PKEY *load_key(const char *uri) -{ - OSSL_STORE_CTX *store; - OSSL_STORE_INFO *info; - EVP_PKEY *key = NULL; - - store = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); - if (store == NULL) { - fprintf(stderr, "Failed to open store: %s\n", uri); - ossl_err_print(); - exit(EXIT_FAILURE); - } - - if (strncmp(uri, "pkcs11:", 7) && strstr(uri, "type=private") == NULL) { - /* This is a workaround for OpenSSL < 3.2.0 where the code fails - * to correctly source public keys unless explicitly requested - * via an expect hint */ - if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PUBKEY) != 1) { - fprintf(stderr, "Failed to expect Public Key File\n"); - exit(EXIT_FAILURE); - } - } - - for (info = OSSL_STORE_load(store); info != NULL; - info = OSSL_STORE_load(store)) { - int type = OSSL_STORE_INFO_get_type(info); - - if (key != NULL) { - fprintf(stderr, "Multiple keys matching URI: %s\n", uri); - exit(EXIT_FAILURE); - } - - switch (type) { - case OSSL_STORE_INFO_PUBKEY: - key = OSSL_STORE_INFO_get1_PUBKEY(info); - break; - case OSSL_STORE_INFO_PKEY: - key = OSSL_STORE_INFO_get1_PKEY(info); - break; - } - OSSL_STORE_INFO_free(info); - } - - if (key == NULL) { - fprintf(stderr, "Failed to load key from URI: %s\n", uri); - ossl_err_print(); - exit(EXIT_FAILURE); - } - OSSL_STORE_close(store); - - return key; -} +#include "util.h" int main(int argc, char *argv[]) { diff --git a/tests/tlsctx.c b/tests/tlsctx.c index 8e159430..882d872d 100644 --- a/tests/tlsctx.c +++ b/tests/tlsctx.c @@ -4,32 +4,104 @@ #include #include #include -#include +#include +#include +#include +#include "util.h" -static void ossl_err_print(void) +static void test_pkcs1_with_tls_padding(void) { - bool first = true; - unsigned long err = 0; - while (true) { - const char *file, *func, *data; - int line; - err = ERR_get_error_all(&file, &line, &func, &data, NULL); - if (err == 0) { - break; - } + EVP_PKEY_CTX *ctx; + EVP_PKEY *prikey; + EVP_PKEY *pubkey; + unsigned char plain[SSL_MAX_MASTER_KEY_LENGTH + 2] = { 0x03, 0x03, 0x01 }; + unsigned char enc[1024]; + unsigned char dec[1024]; + size_t enclen; + size_t declen; + unsigned int ver = 0x0303; + const OSSL_PARAM ver_params[] = { + OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, &ver), + OSSL_PARAM_END + }; + int err; - char buf[1024]; - ERR_error_string_n(err, buf, sizeof(buf)); + pubkey = load_key(getenv("PUBURI")); - const char *fmt = - first ? ": %s (in function %s in %s:%d): %s\n" - : " caused by: %s (in function %s in %s:%d): %s\n"; - fprintf(stderr, fmt, buf, func, file, line, data); + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, pubkey, NULL); + if (!ctx) { + fprintf(stderr, "Failed to init pkey ctx for puburi\n"); + ossl_err_print(); + exit(EXIT_FAILURE); + } + err = EVP_PKEY_encrypt_init(ctx); + if (err != 1) { + fprintf(stderr, "Failed to init encrypt ctx\n"); + ossl_err_print(); + exit(EXIT_FAILURE); + } + err = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING); + if (err != 1) { + fprintf(stderr, "Failed to set padding on encrypt ctx\n"); + ossl_err_print(); + exit(EXIT_FAILURE); + } + + enclen = sizeof(enc); + err = EVP_PKEY_encrypt(ctx, enc, &enclen, plain, sizeof(plain)); + if (err != 1) { + fprintf(stderr, "Failed to encrypt TLS master key\n"); + ossl_err_print(); + exit(EXIT_FAILURE); + } + + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(pubkey); + + prikey = load_key(getenv("PRIURI")); + + ctx = EVP_PKEY_CTX_new_from_pkey(NULL, prikey, NULL); + if (!ctx) { + fprintf(stderr, "Failed to init pkey ctx for priuri\n"); + ossl_err_print(); + exit(EXIT_FAILURE); + } + err = EVP_PKEY_decrypt_init(ctx); + if (err != 1) { + fprintf(stderr, "Failed to init decrypt ctx\n"); + ossl_err_print(); + exit(EXIT_FAILURE); + } + err = EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_WITH_TLS_PADDING); + if (err != 1) { + fprintf(stderr, "Failed to set padding on decrypt ctx\n"); + ossl_err_print(); + exit(EXIT_FAILURE); + } - first = false; + err = EVP_PKEY_CTX_set_params(ctx, ver_params); + if (err != 1) { + fprintf(stderr, "Failed to set version params\n"); + ossl_err_print(); + exit(EXIT_FAILURE); } - if (first) { - fprintf(stderr, "\n"); + + declen = sizeof(dec); + err = EVP_PKEY_decrypt(ctx, dec, &declen, enc, enclen); + if (err != 1) { + fprintf(stderr, "Failed to decrypt TLS master key\n"); + ossl_err_print(); + exit(EXIT_FAILURE); + } + + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(prikey); + + if ((declen != sizeof(plain) - 2) + || (memcmp(plain + 2, dec, declen) != 0)) { + fprintf(stderr, "Fail, decrypted master secret differs from input\n"); + ossl_err_print(); + exit(EXIT_FAILURE); } } @@ -48,5 +120,7 @@ int main(int argc, char *argv[]) SSL_CTX_free(ctx); + test_pkcs1_with_tls_padding(); + exit(EXIT_SUCCESS); } diff --git a/tests/util.c b/tests/util.c new file mode 100644 index 00000000..eedb4cb7 --- /dev/null +++ b/tests/util.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2024 Simo Sorce + SPDX-License-Identifier: Apache-2.0 */ + +#include +#include +#include +#include +#include "util.h" + +void ossl_err_print(void) +{ + bool first = true; + unsigned long err = 0; + while (true) { + const char *file, *func, *data; + int line; + err = ERR_get_error_all(&file, &line, &func, &data, NULL); + if (err == 0) { + break; + } + + char buf[1024]; + ERR_error_string_n(err, buf, sizeof(buf)); + + const char *fmt = + first ? ": %s (in function %s in %s:%d): %s\n" + : " caused by: %s (in function %s in %s:%d): %s\n"; + fprintf(stderr, fmt, buf, func, file, line, data); + + first = false; + } + if (first) { + fprintf(stderr, "\n"); + } +} + +EVP_PKEY *load_key(const char *uri) +{ + OSSL_STORE_CTX *store; + OSSL_STORE_INFO *info; + EVP_PKEY *key = NULL; + + if (!uri) { + fprintf(stderr, "Invalid NULL uri"); + ossl_err_print(); + exit(EXIT_FAILURE); + } + + store = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); + if (store == NULL) { + fprintf(stderr, "Failed to open store: %s\n", uri); + ossl_err_print(); + exit(EXIT_FAILURE); + } + + if (strncmp(uri, "pkcs11:", 7) && strstr(uri, "type=private") == NULL) { + /* This is a workaround for OpenSSL < 3.2.0 where the code fails + * to correctly source public keys unless explicitly requested + * via an expect hint */ + if (OSSL_STORE_expect(store, OSSL_STORE_INFO_PUBKEY) != 1) { + fprintf(stderr, "Failed to expect Public Key File\n"); + exit(EXIT_FAILURE); + } + } + + for (info = OSSL_STORE_load(store); info != NULL; + info = OSSL_STORE_load(store)) { + int type = OSSL_STORE_INFO_get_type(info); + + if (key != NULL) { + fprintf(stderr, "Multiple keys matching URI: %s\n", uri); + exit(EXIT_FAILURE); + } + + switch (type) { + case OSSL_STORE_INFO_PUBKEY: + key = OSSL_STORE_INFO_get1_PUBKEY(info); + break; + case OSSL_STORE_INFO_PKEY: + key = OSSL_STORE_INFO_get1_PKEY(info); + break; + } + OSSL_STORE_INFO_free(info); + } + + if (key == NULL) { + fprintf(stderr, "Failed to load key from URI: %s\n", uri); + ossl_err_print(); + exit(EXIT_FAILURE); + } + OSSL_STORE_close(store); + + return key; +} diff --git a/tests/util.h b/tests/util.h new file mode 100644 index 00000000..1fdc9a13 --- /dev/null +++ b/tests/util.h @@ -0,0 +1,5 @@ +/* Copyright (C) 2024 Simo Sorce + SPDX-License-Identifier: Apache-2.0 */ + +void ossl_err_print(void); +EVP_PKEY *load_key(const char *uri);