From 708083e96fbea05a2bc1a5ee99e93df646ad2e82 Mon Sep 17 00:00:00 2001 From: Florian Wernli Date: Wed, 6 Dec 2023 13:02:35 +0100 Subject: [PATCH] pk11_uri: pem encoder and decoder Register an encoder for writing the PrivateKeyInfo as PEM. The encoder writes the pcsk11-uri to the PEM file. The file has the following structure: { ASN1_OBJECT *type; // set to "2.5.4.83" ASN1_UTF8STRING *uri; // pkcs11-uri as produced by p11prov_key_to_uri() } The feature has to be enabled explicitly by setting pkcs11-module-encode-key-uri-to-pem. Signed-off-by: Florian Wernli --- .github/workflows/shellcheck.yml | 1 + src/Makefile.am | 5 + src/decoder.c | 358 +++++++++++++++++++++++++++++++ src/decoder.h | 36 ++++ src/encoder.c | 140 ++++++++++++ src/encoder.h | 2 + src/pk11_uri.c | 7 + src/pk11_uri.gen.c | 71 ++++++ src/pk11_uri.h | 28 +++ src/pk11_uri.pre | 23 ++ src/provider.c | 51 ++++- src/provider.h | 3 + tests/Makefile.am | 5 +- tests/openssl.cnf.in | 1 + tests/tpem_encoder | 110 ++++++++++ 15 files changed, 834 insertions(+), 7 deletions(-) create mode 100644 src/decoder.c create mode 100644 src/decoder.h create mode 100644 src/pk11_uri.c create mode 100644 src/pk11_uri.gen.c create mode 100644 src/pk11_uri.h create mode 100644 src/pk11_uri.pre create mode 100755 tests/tpem_encoder diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 94385a0d..84a0544f 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -30,6 +30,7 @@ jobs: thkdf toaepsha2 top_state + tpem_encoder tpubkey trand trsapss diff --git a/src/Makefile.am b/src/Makefile.am index c3110a95..a5db81df 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,10 +5,12 @@ noinst_HEADERS = \ asymmetric_cipher.h \ debug.h \ encoder.h \ + decoder.h \ digests.h \ exchange.h \ kdf.h \ keymgmt.h \ + pk11_uri.h \ interface.h \ objects.h \ pkcs11.h \ @@ -30,10 +32,12 @@ pkcs11_la_SOURCES = \ asymmetric_cipher.c \ debug.c \ encoder.c \ + decoder.c \ digests.c \ exchange.c \ kdf.c \ keymgmt.c \ + pk11_uri.c \ interface.c \ objects.c \ provider.h \ @@ -51,6 +55,7 @@ pkcs11_la_SOURCES = \ EXTRA_DIST = \ interface.gen.c \ encoder.gen.c \ + pk11_uri.gen.c \ $(NULL) pkcs11_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS) -Wall -Werror diff --git a/src/decoder.c b/src/decoder.c new file mode 100644 index 00000000..822c179f --- /dev/null +++ b/src/decoder.c @@ -0,0 +1,358 @@ +/* Copyright (C) 2023 Simo Sorce + SPDX-License-Identifier: Apache-2.0 +*/ + +#include "provider.h" +#include "decoder.h" +#include "pk11_uri.h" +#include +#include +#include +#include +#include +#include + +#define MAX_OID_LEN 64 + +typedef struct p11prov_decoder_ctx { + P11PROV_CTX *provctx; + P11PROV_OBJ *object; + bool invalid; +} P11PROV_DECODER_CTX; + +static bool decoder_ctx_accepts_decoded_object(P11PROV_DECODER_CTX *ctx) +{ + return (!ctx->invalid) && (ctx->object == NULL); +} + +static void decoder_ctx_object_free(struct p11prov_decoder_ctx *ctx) +{ + if (ctx && ctx->object) { + p11prov_obj_free(ctx->object); + ctx->object = NULL; + } +} + +static void *p11prov_decoder_newctx(void *provctx) +{ + P11PROV_CTX *cprov; + P11PROV_DECODER_CTX *dctx; + cprov = provctx; + dctx = OPENSSL_zalloc(sizeof(P11PROV_DECODER_CTX)); + if (dctx == NULL) { + return NULL; + } + + dctx->provctx = cprov; + return dctx; +} + +static void p11prov_decoder_freectx(void *inctx) +{ + P11PROV_DECODER_CTX *ctx = inctx; + + decoder_ctx_object_free(ctx); + OPENSSL_clear_free(ctx, sizeof(P11PROV_DECODER_CTX)); +} + +static CK_RV p11prov_decoder_ctx_store_obj(void *pctx, P11PROV_OBJ *obj) +{ + P11PROV_DECODER_CTX *ctx = pctx; + + if (!decoder_ctx_accepts_decoded_object(ctx)) { + P11PROV_raise(ctx->provctx, CKR_GENERAL_ERROR, + "Invalid decoder context"); + ctx->invalid = 1; + decoder_ctx_object_free(ctx); + p11prov_obj_free(obj); + return CKR_GENERAL_ERROR; + } + + P11PROV_debug("Adding object (handle:%lu)", p11prov_obj_get_handle(obj)); + if (p11prov_obj_get_class(obj) != CKO_PRIVATE_KEY) { + P11PROV_raise(ctx->provctx, CKR_ARGUMENTS_BAD, + "Object must be private key"); + p11prov_obj_free(obj); + return CKR_ARGUMENTS_BAD; + } + + ctx->object = obj; + + return CKR_OK; +} + +static CK_RV p11prov_decoder_load_pkey(P11PROV_DECODER_CTX *ctx, + const char *inuri, + OSSL_PASSPHRASE_CALLBACK *pw_cb, + void *pw_cbarg) +{ + P11PROV_URI *parsed_uri = NULL; + CK_RV ret = CKR_GENERAL_ERROR; + P11PROV_SESSION *session = NULL; + CK_SLOT_ID slotid = CK_UNAVAILABLE_INFORMATION; + CK_SLOT_ID nextid = CK_UNAVAILABLE_INFORMATION; + + if (!decoder_ctx_accepts_decoded_object(ctx)) { + P11PROV_debug("Invalid context state"); + P11PROV_raise(ctx->provctx, CKR_GENERAL_ERROR, "Invalid initial state"); + goto done; + } + + parsed_uri = p11prov_parse_uri(ctx->provctx, inuri); + if (parsed_uri == NULL) { + P11PROV_raise(ctx->provctx, CKR_GENERAL_ERROR, "Failed to parse URI"); + goto done; + } + + ret = p11prov_ctx_status(ctx->provctx); + if (ret != CKR_OK) { + P11PROV_raise(ctx->provctx, ret, "Invalid context status"); + goto done; + } + + p11prov_set_error_mark(ctx->provctx); + do { + nextid = CK_UNAVAILABLE_INFORMATION; + p11prov_return_session(session); + if (!decoder_ctx_accepts_decoded_object(ctx)) { + break; + } + + ret = p11prov_get_session(ctx->provctx, &slotid, &nextid, parsed_uri, + CK_UNAVAILABLE_INFORMATION, pw_cb, pw_cbarg, + true, false, &session); + if (ret != CKR_OK) { + P11PROV_debug( + "Failed to get session to load keys (slotid=%lx, ret=%lx)", + slotid, ret); + slotid = nextid; + continue; + } + + ret = p11prov_obj_find(ctx->provctx, session, slotid, parsed_uri, + p11prov_decoder_ctx_store_obj, ctx); + if (ret != CKR_OK) { + P11PROV_debug( + "Failed to find object on (slotid=%lx, session=%lx, ret=%lx)", + slotid, session, ret); + slotid = nextid; + continue; + } + slotid = nextid; + } while (nextid != CK_UNAVAILABLE_INFORMATION); + ret = CKR_OK; + p11prov_pop_error_to_mark(ctx->provctx); + p11prov_clear_last_error_mark(ctx->provctx); + + if (ctx->invalid) { + ret = CKR_GENERAL_ERROR; + P11PROV_raise(ctx->provctx, ret, "Invalid context status"); + goto done; + } + + if (!ctx->object) { + ret = CKR_GENERAL_ERROR; + P11PROV_raise(ctx->provctx, ret, "No matching object stored"); + goto done; + } + + if (p11prov_obj_get_class(ctx->object) != CKO_PRIVATE_KEY) { + ret = CKR_ARGUMENTS_BAD; + P11PROV_raise(ctx->provctx, ret, + "Referenced object is not a private key"); + goto done; + } + +done: + p11prov_uri_free(parsed_uri); + p11prov_return_session(session); + if (ret != CKR_OK) { + decoder_ctx_object_free(ctx); + } + + P11PROV_debug("Done (result:%d)", ret); + return ret; +} + +static int +p11prov_decoder_decode_p11pkey(CK_KEY_TYPE desired_key_type, void *inctx, + OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + P11PROV_DECODER_CTX *ctx = inctx; + P11PROV_PK11_URI *key = NULL; + BIO *bin; + int ret = 0; + const char *uri = NULL; + + P11PROV_debug("P11 KEY DECODER DECODE (selection:0x%x)", selection); + if ((bin = BIO_new_from_core_bio(p11prov_ctx_get_libctx(ctx->provctx), cin)) + == NULL) { + P11PROV_debug("P11 KEY DECODER BIO_new_from_core_bio failed"); + goto done; + } + + const char *data_type = NULL; + switch (desired_key_type) { + case CKK_RSA: + data_type = P11PROV_NAME_RSA; + break; + case CKK_EC: + data_type = P11PROV_NAME_EC; + break; + default: + ret = 0; + P11PROV_raise(ctx->provctx, CKR_ARGUMENTS_BAD, "Unsupported key type"); + goto done; + } + + const unsigned char *der; + long der_len = BIO_get_mem_data(bin, &der); + if (der_len <= 0) { + P11PROV_debug("P11 KEY DECODER BIO_get_mem_data failed"); + ret = 1; + goto done; + } + if ((key = d2i_P11PROV_PK11_URI(NULL, &der, der_len)) == NULL) { + P11PROV_debug("P11 KEY DECODER d2i_P11PROV_PK11_URI failed"); + ret = 1; + goto done; + } + + char oid_txt[MAX_OID_LEN]; + if (OBJ_obj2txt(oid_txt, sizeof(oid_txt), key->type, 1) > 0) { + P11PROV_debug("P11 KEY DECODER got OID %s", oid_txt); + } else { + P11PROV_debug("P11 KEY DECODER OBJ_obj2txt failed"); + goto done; + } + + uri = (const char *)ASN1_STRING_get0_data(key->uri); + if (uri == NULL) { + P11PROV_raise(ctx->provctx, CKR_GENERAL_ERROR, "Failed to get URI"); + goto done; + } + + if (p11prov_decoder_load_pkey(ctx, uri, pw_cb, pw_cbarg) != CKR_OK) { + P11PROV_debug("P11 KEY DECODER p11prov_decoder_load_key failed"); + goto done; + } + + if (p11prov_obj_get_key_type(ctx->object) != desired_key_type) { + P11PROV_debug( + "P11 KEY DECODER p11prov_decoder_load_key wrong key type"); + ret = 1; + goto done; + } + + P11PROV_debug("P11 KEY DECODER p11prov_decoder_load_key OK"); + + void *key_reference = NULL; + size_t key_reference_sz = 0; + p11prov_obj_to_store_reference(ctx->object, &key_reference, + &key_reference_sz); + + int object_type = OSSL_OBJECT_PKEY; + OSSL_PARAM params[4]; + params[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + (char *)data_type, 0); + /* The address of the key becomes the octet string */ + params[2] = OSSL_PARAM_construct_octet_string( + OSSL_OBJECT_PARAM_REFERENCE, key_reference, key_reference_sz); + params[3] = OSSL_PARAM_construct_end(); + object_cb(params, object_cbarg); + +done: + decoder_ctx_object_free(ctx); + P11PROV_PK11_URI_free(key); + BIO_free(bin); + P11PROV_debug("P11 KEY DECODER RESULT=%d", ret); + return ret; +} + +static int p11prov_der_decoder_p11_rsa_decode( + void *inctx, OSSL_CORE_BIO *cin, int selection, OSSL_CALLBACK *object_cb, + void *object_cbarg, OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + return p11prov_decoder_decode_p11pkey(CKK_RSA, inctx, cin, selection, + object_cb, object_cbarg, pw_cb, + pw_cbarg); +} + +const OSSL_DISPATCH p11prov_der_decoder_p11_rsa_functions[] = { + DISPATCH_BASE_DECODER_ELEM(NEWCTX, newctx), + DISPATCH_BASE_DECODER_ELEM(FREECTX, freectx), + DISPATCH_DECODER_ELEM(DECODE, der, p11, rsa, decode), + { 0, NULL } +}; + +static int p11prov_der_decoder_p11_ec_decode( + void *inctx, OSSL_CORE_BIO *cin, int selection, OSSL_CALLBACK *object_cb, + void *object_cbarg, OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + return p11prov_decoder_decode_p11pkey(CKK_EC, inctx, cin, selection, + object_cb, object_cbarg, pw_cb, + pw_cbarg); +} + +const OSSL_DISPATCH p11prov_der_decoder_p11_ec_functions[] = { + DISPATCH_BASE_DECODER_ELEM(NEWCTX, newctx), + DISPATCH_BASE_DECODER_ELEM(FREECTX, freectx), + DISPATCH_DECODER_ELEM(DECODE, der, p11, ec, decode), + { 0, NULL } +}; + +static int p11prov_pem_decoder_p11_der_decode( + void *inctx, OSSL_CORE_BIO *cin, int selection, OSSL_CALLBACK *object_cb, + void *object_cbarg, OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + + BIO *bin; + char *pem_name; + char *pem_header; + unsigned char *der_data; + long der_len; + OSSL_PARAM params[3]; + int ret; + P11PROV_DECODER_CTX *ctx = inctx; + + P11PROV_debug("DER DECODER DECODE (selection:0x%x)", selection); + + if ((bin = BIO_new_from_core_bio(p11prov_ctx_get_libctx(ctx->provctx), cin)) + == NULL) { + P11PROV_debug("BIO_new_from_core_bio failed"); + return 0; + } + P11PROV_debug("DER DECODER PEM_read_pio (fpos:%u)", BIO_tell(bin)); + + if (PEM_read_bio(bin, &pem_name, &pem_header, &der_data, &der_len) > 0 + && strcmp(pem_name, P11PROV_PRIVKEY_PEM_NAME) == 0) { + params[0] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, + der_data, der_len); + params[1] = OSSL_PARAM_construct_utf8_string( + OSSL_OBJECT_PARAM_DATA_STRUCTURE, + (char *)P11PROV_PK11_URI_STRUCTURE, 0); + params[2] = OSSL_PARAM_construct_end(); + ret = object_cb(params, object_cbarg); + } else { + /* We return "empty handed". This is not an error. */ + ret = 1; + } + + OPENSSL_free(pem_name); + OPENSSL_free(pem_header); + OPENSSL_free(der_data); + BIO_free(bin); + + P11PROV_debug("DER DECODER RESULT=%d", ret); + return ret; +} + +const OSSL_DISPATCH p11prov_pem_decoder_p11_der_functions[] = { + DISPATCH_BASE_DECODER_ELEM(NEWCTX, newctx), + DISPATCH_BASE_DECODER_ELEM(FREECTX, freectx), + DISPATCH_DECODER_ELEM(DECODE, pem, p11, der, decode), + { 0, NULL } +}; diff --git a/src/decoder.h b/src/decoder.h new file mode 100644 index 00000000..2004617f --- /dev/null +++ b/src/decoder.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2022 Simo Sorce + SPDX-License-Identifier: Apache-2.0 */ + +#ifndef _DECODER_H +#define _DECODER_H + +#include + +/* DECODERs */ +#define DISPATCH_TEXT_DECODER_FN(type, name) \ + static OSSL_FUNC_DECODER_##name##_fn p11prov_##type##_DECODER_##name##_text +#define DISPATCH_TEXT_DECODER_ELEM(NAME, type, name) \ + { \ + OSSL_FUNC_DECODER_##NAME, \ + (void (*)(void))p11prov_##type##_DECODER_##name \ + } +#define DISPATCH_BASE_DECODER_FN(name) \ + DECL_DISPATCH_FUNC(DECODER, p11prov_decoder_, name) +#define DISPATCH_BASE_DECODER_ELEM(NAME, name) \ + { \ + OSSL_FUNC_DECODER_##NAME, (void (*)(void))p11prov_decoder_##name \ + } +#define DISPATCH_DECODER_FN(type, structure, format, name) \ + DECL_DISPATCH_FUNC(DECODER, \ + p11prov_##type##_decoder_##structure##_##format, name) +#define DISPATCH_DECODER_ELEM(NAME, type, structure, format, name) \ + { \ + OSSL_FUNC_DECODER_##NAME, \ + (void (*)( \ + void))p11prov_##type##_decoder_##structure##_##format##_##name \ + } +extern const OSSL_DISPATCH p11prov_der_decoder_p11_rsa_functions[]; +extern const OSSL_DISPATCH p11prov_der_decoder_p11_ec_functions[]; +extern const OSSL_DISPATCH p11prov_pem_decoder_p11_der_functions[]; + +#endif /* _DECODER_H */ diff --git a/src/encoder.c b/src/encoder.c index 1de54cc6..bab9d903 100644 --- a/src/encoder.c +++ b/src/encoder.c @@ -2,6 +2,7 @@ SPDX-License-Identifier: Apache-2.0 */ #include "provider.h" +#include "pk11_uri.h" #include #include #include @@ -463,6 +464,118 @@ const OSSL_DISPATCH p11prov_rsa_encoder_spki_pem_functions[] = { { 0, NULL }, }; +static P11PROV_PK11_URI *p11prov_encoder_private_key_to_asn1(P11PROV_CTX *pctx, + P11PROV_OBJ *key) +{ + P11PROV_PK11_URI *out = NULL; + + char *uri = p11prov_key_to_uri(pctx, key); + if (!uri) { + goto error; + } + + size_t uri_len = strlen(uri); + P11PROV_debug("uri=%s", uri); + + out = P11PROV_PK11_URI_new(); + if (!out) { + goto error; + } + + out->type = OBJ_txt2obj(P11PROV_OID_URI, 1); + if (!ASN1_STRING_set(out->uri, uri, uri_len)) { + goto error; + } + + goto done; + +error: + P11PROV_PK11_URI_free(out); + out = NULL; + +done: + if (uri) { + free(uri); + } + return out; +} + +static int p11prov_encoder_private_key_write_pem( + CK_KEY_TYPE expected_key_type, void *inctx, OSSL_CORE_BIO *cbio, + const void *inkey, const OSSL_PARAM key_abstract[], int selection, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + struct p11prov_encoder_ctx *ctx = (struct p11prov_encoder_ctx *)inctx; + P11PROV_OBJ *key = (P11PROV_OBJ *)inkey; + CK_KEY_TYPE key_type; + P11PROV_PK11_URI *asn1 = NULL; + BIO *out = NULL; + int ret; + + key_type = p11prov_obj_get_key_type(key); + if (key_type != expected_key_type) { + P11PROV_raise(ctx->provctx, CKR_GENERAL_ERROR, + "Key type mismatch (actual:%lu,expected:%lu)", key_type, + expected_key_type); + ret = RET_OSSL_ERR; + goto done; + } + + asn1 = p11prov_encoder_private_key_to_asn1(ctx->provctx, key); + if (!asn1) { + P11PROV_raise(ctx->provctx, CKR_GENERAL_ERROR, + "Failed to encode private key"); + ret = RET_OSSL_ERR; + goto done; + } + + out = BIO_new_from_core_bio(p11prov_ctx_get_libctx(ctx->provctx), cbio); + if (!out) { + P11PROV_raise(ctx->provctx, CKR_GENERAL_ERROR, "Failed to init BIO"); + ret = RET_OSSL_ERR; + goto done; + } + + ret = PEM_write_bio_P11PROV_PK11_URI(out, asn1); + if (ret != RET_OSSL_OK) { + P11PROV_raise(ctx->provctx, CKR_GENERAL_ERROR, + "Failed to write BIO PEM"); + goto done; + } + +done: + P11PROV_PK11_URI_free(asn1); + BIO_free(out); + return ret; +} + +static int p11prov_rsa_encoder_priv_key_info_pem_does_selection(void *inctx, + int selection) +{ + if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { + return RET_OSSL_OK; + } + return RET_OSSL_ERR; +} + +static int p11prov_rsa_encoder_priv_key_info_pem_encode( + void *inctx, OSSL_CORE_BIO *cbio, const void *inkey, + const OSSL_PARAM key_abstract[], int selection, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + return p11prov_encoder_private_key_write_pem( + CKK_RSA, inctx, cbio, inkey, key_abstract, selection, cb, cbarg); +} + +const OSSL_DISPATCH p11prov_rsa_encoder_priv_key_info_pem_functions[] = { + DISPATCH_BASE_ENCODER_ELEM(NEWCTX, newctx), + DISPATCH_BASE_ENCODER_ELEM(FREECTX, freectx), + DISPATCH_ENCODER_ELEM(DOES_SELECTION, rsa, priv_key_info, pem, + does_selection), + DISPATCH_ENCODER_ELEM(ENCODE, rsa, priv_key_info, pem, encode), + { 0, NULL }, +}; + /* ECDSA */ struct ecdsa_key_point { @@ -802,3 +915,30 @@ const OSSL_DISPATCH p11prov_ec_encoder_text_functions[] = { DISPATCH_TEXT_ENCODER_ELEM(ENCODE, ec, encode_text), { 0, NULL }, }; + +static int p11prov_ec_encoder_priv_key_info_pem_does_selection(void *inctx, + int selection) +{ + if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { + return RET_OSSL_OK; + } + return RET_OSSL_ERR; +} + +static int p11prov_ec_encoder_priv_key_info_pem_encode( + void *inctx, OSSL_CORE_BIO *cbio, const void *inkey, + const OSSL_PARAM key_abstract[], int selection, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + return p11prov_encoder_private_key_write_pem( + CKK_EC, inctx, cbio, inkey, key_abstract, selection, cb, cbarg); +} + +const OSSL_DISPATCH p11prov_ec_encoder_priv_key_info_pem_functions[] = { + DISPATCH_BASE_ENCODER_ELEM(NEWCTX, newctx), + DISPATCH_BASE_ENCODER_ELEM(FREECTX, freectx), + DISPATCH_ENCODER_ELEM(DOES_SELECTION, ec, priv_key_info, pem, + does_selection), + DISPATCH_ENCODER_ELEM(ENCODE, ec, priv_key_info, pem, encode), + { 0, NULL }, +}; diff --git a/src/encoder.h b/src/encoder.h index 1e4b2944..02684441 100644 --- a/src/encoder.h +++ b/src/encoder.h @@ -32,9 +32,11 @@ extern const OSSL_DISPATCH p11prov_rsa_encoder_pkcs1_der_functions[]; extern const OSSL_DISPATCH p11prov_rsa_encoder_pkcs1_pem_functions[]; extern const OSSL_DISPATCH p11prov_rsa_encoder_spki_der_functions[]; extern const OSSL_DISPATCH p11prov_rsa_encoder_spki_pem_functions[]; +extern const OSSL_DISPATCH p11prov_rsa_encoder_priv_key_info_pem_functions[]; extern const OSSL_DISPATCH p11prov_ec_encoder_text_functions[]; extern const OSSL_DISPATCH p11prov_ec_encoder_pkcs1_der_functions[]; extern const OSSL_DISPATCH p11prov_ec_encoder_pkcs1_pem_functions[]; extern const OSSL_DISPATCH p11prov_ec_encoder_spki_der_functions[]; +extern const OSSL_DISPATCH p11prov_ec_encoder_priv_key_info_pem_functions[]; #endif /* _ENCODER_H */ diff --git a/src/pk11_uri.c b/src/pk11_uri.c new file mode 100644 index 00000000..43822aee --- /dev/null +++ b/src/pk11_uri.c @@ -0,0 +1,7 @@ +/* Copyright (C) 2022 Simo Sorce + SPDX-License-Identifier: Apache-2.0 */ + +#include "pk11_uri.h" +#include + +#include "pk11_uri.gen.c" diff --git a/src/pk11_uri.gen.c b/src/pk11_uri.gen.c new file mode 100644 index 00000000..1fca884e --- /dev/null +++ b/src/pk11_uri.gen.c @@ -0,0 +1,71 @@ +/* DO NOT EDIT, autogenerated from src/pk11_uri.pre */ +/* Modify src/pk11_uri.pre then run make generate-code */ + +extern P11PROV_PK11_URI *P11PROV_PK11_URI_new(void); +extern void P11PROV_PK11_URI_free(P11PROV_PK11_URI *a); +extern P11PROV_PK11_URI * +d2i_P11PROV_PK11_URI(P11PROV_PK11_URI **a, const unsigned char **in, long len); +extern int i2d_P11PROV_PK11_URI(const P11PROV_PK11_URI *a, unsigned char **out); +extern const ASN1_ITEM *P11PROV_PK11_URI_it(void); + +P11PROV_PK11_URI +*d2i_P11PROV_PK11_URI(P11PROV_PK11_URI **a, const unsigned char **in, long len) +{ + return (P11PROV_PK11_URI *)ASN1_item_d2i((ASN1_VALUE **)a, in, len, + (P11PROV_PK11_URI_it())); +} +int i2d_P11PROV_PK11_URI(const P11PROV_PK11_URI *a, unsigned char **out) +{ + return ASN1_item_i2d((const ASN1_VALUE *)a, out, (P11PROV_PK11_URI_it())); +} +P11PROV_PK11_URI +*P11PROV_PK11_URI_new(void) +{ + return (P11PROV_PK11_URI *)ASN1_item_new((P11PROV_PK11_URI_it())); +} +void P11PROV_PK11_URI_free(P11PROV_PK11_URI *a) +{ + ASN1_item_free((ASN1_VALUE *)a, (P11PROV_PK11_URI_it())); +} + +static const ASN1_TEMPLATE P11PROV_PK11_URI_seq_tt[] = { + + { (0), (0), __builtin_offsetof(P11PROV_PK11_URI, type), "type", + (ASN1_OBJECT_it) }, + + { (0), (0), __builtin_offsetof(P11PROV_PK11_URI, uri), "uri", + (ASN1_UTF8STRING_it) }, +}; +const ASN1_ITEM *P11PROV_PK11_URI_it(void) +{ + static const ASN1_ITEM local_it = { 0x1, + 16, + P11PROV_PK11_URI_seq_tt, + sizeof(P11PROV_PK11_URI_seq_tt) + / sizeof(ASN1_TEMPLATE), + ((void *)0), + sizeof(P11PROV_PK11_URI), + "P11PROV_PK11_URI" }; + return &local_it; +} + +extern int PEM_write_bio_P11PROV_PK11_URI(BIO *out, const P11PROV_PK11_URI *x); +int PEM_write_bio_P11PROV_PK11_URI(BIO *out, const P11PROV_PK11_URI *x) +{ + return PEM_ASN1_write_bio((i2d_of_void *)i2d_P11PROV_PK11_URI, + P11PROV_PRIVKEY_PEM_NAME, out, x, ((void *)0), + ((void *)0), 0, ((void *)0), ((void *)0)); +} + +extern P11PROV_PK11_URI *PEM_read_bio_P11PROV_PK11_URI(BIO *out, + P11PROV_PK11_URI **x, + pem_password_cb *cb, + void *u); + +P11PROV_PK11_URI +*PEM_read_bio_P11PROV_PK11_URI(BIO *bp, P11PROV_PK11_URI **x, + pem_password_cb *cb, void *u) +{ + return PEM_ASN1_read_bio((d2i_of_void *)d2i_P11PROV_PK11_URI, + P11PROV_PRIVKEY_PEM_NAME, bp, (void **)x, cb, u); +} diff --git a/src/pk11_uri.h b/src/pk11_uri.h new file mode 100644 index 00000000..255b515e --- /dev/null +++ b/src/pk11_uri.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2022 Simo Sorce + SPDX-License-Identifier: Apache-2.0 */ + +#ifndef _PK11_URI_H +#define _PK11_URI_H + +#include + +#define P11PROV_OID_URI "2.5.4.83" /* TODO: find a more appropriate oId */ +#define P11PROV_PK11_URI_STRUCTURE "pk11-uri" +#define P11PROV_PRIVKEY_PEM_NAME "PRIVATE KEY PK11-URI" + +typedef struct { + ASN1_OBJECT *type; + ASN1_UTF8STRING *uri; +} P11PROV_PK11_URI; + +extern P11PROV_PK11_URI *P11PROV_PK11_URI_new(void); +extern void P11PROV_PK11_URI_free(P11PROV_PK11_URI *a); +extern P11PROV_PK11_URI * +d2i_P11PROV_PK11_URI(P11PROV_PK11_URI **a, const unsigned char **in, long len); +extern P11PROV_PK11_URI *PEM_read_bio_P11PROV_PK11_URI(BIO *out, + P11PROV_PK11_URI **x, + pem_password_cb *cb, + void *u); +extern int PEM_write_bio_P11PROV_PK11_URI(BIO *out, const P11PROV_PK11_URI *x); + +#endif diff --git a/src/pk11_uri.pre b/src/pk11_uri.pre new file mode 100644 index 00000000..38a1daf0 --- /dev/null +++ b/src/pk11_uri.pre @@ -0,0 +1,23 @@ +/* Copyright (C) 2022 Simo Sorce + SPDX-License-Identifier: Apache-2.0 */ + +#include +#include + +BEGIN: + +DECLARE_ASN1_FUNCTIONS(P11PROV_PK11_URI) +IMPLEMENT_ASN1_FUNCTIONS(P11PROV_PK11_URI) + +ASN1_SEQUENCE(P11PROV_PK11_URI) = { + ASN1_SIMPLE(P11PROV_PK11_URI, type, ASN1_OBJECT), + ASN1_SIMPLE(P11PROV_PK11_URI, uri, ASN1_UTF8STRING), +} ASN1_SEQUENCE_END(P11PROV_PK11_URI) + +DECLARE_PEM_write_bio(P11PROV_PK11_URI, P11PROV_PK11_URI) +IMPLEMENT_PEM_write_bio(P11PROV_PK11_URI, P11PROV_PK11_URI, + P11PROV_PRIVKEY_PEM_NAME, P11PROV_PK11_URI) + +DECLARE_PEM_read_bio(P11PROV_PK11_URI, P11PROV_PK11_URI) +IMPLEMENT_PEM_read_bio(P11PROV_PK11_URI, P11PROV_PK11_URI, + P11PROV_PRIVKEY_PEM_NAME, P11PROV_PK11_URI) diff --git a/src/provider.c b/src/provider.c index 0d416fea..1edaf7a6 100644 --- a/src/provider.c +++ b/src/provider.c @@ -2,6 +2,7 @@ SPDX-License-Identifier: Apache-2.0 */ #include "provider.h" +#include "decoder.h" #include #include @@ -29,6 +30,7 @@ struct p11prov_ctx { bool cache_pins; int cache_keys; int cache_sessions; + bool encode_pkey_as_pk11_uri; /* TODO: ui_method */ /* TODO: fork id */ @@ -1075,6 +1077,15 @@ static CK_RV operations_init(P11PROV_CTX *ctx) ADD_ALGO_EXT(EC, encoder, "provider=pkcs11,output=der,structure=SubjectPublicKeyInfo", p11prov_ec_encoder_spki_der_functions); + if (ctx->encode_pkey_as_pk11_uri) { + ADD_ALGO_EXT(RSAKEY, encoder, + "provider=pkcs11,output=pem,structure=PrivateKeyInfo", + p11prov_rsa_encoder_priv_key_info_pem_functions); + ADD_ALGO_EXT(EC, encoder, + "provider=pkcs11,output=pem,structure=PrivateKeyInfo", + p11prov_ec_encoder_priv_key_info_pem_functions); + } + TERM_ALGO(encoder); /* handle random */ @@ -1113,6 +1124,18 @@ static const OSSL_ALGORITHM p11prov_store[] = { { NULL, NULL, NULL, NULL }, }; +static const OSSL_ALGORITHM p11prov_decoders[] = { + { "DER", "provider=pkcs11,input=pem", + p11prov_pem_decoder_p11_der_functions }, + { "RSA:rsaEncryption", + "provider=pkcs11,input=der,structure=" P11PROV_PK11_URI_STRUCTURE, + p11prov_der_decoder_p11_rsa_functions }, + { "EC:id-ecPublicKey", + "provider=pkcs11,input=der,structure=" P11PROV_PK11_URI_STRUCTURE, + p11prov_der_decoder_p11_ec_functions }, + { NULL, NULL, NULL } +}; + static const OSSL_ALGORITHM * p11prov_query_operation(void *provctx, int operation_id, int *no_cache) { @@ -1135,6 +1158,8 @@ p11prov_query_operation(void *provctx, int operation_id, int *no_cache) return ctx->op_asym_cipher; case OSSL_OP_ENCODER: return ctx->op_encoder; + case OSSL_OP_DECODER: + return p11prov_decoders; case OSSL_OP_STORE: return p11prov_store; } @@ -1295,17 +1320,24 @@ enum p11prov_cfg_enum { P11PROV_CFG_CACHE_KEYS, P11PROV_CFG_QUIRKS, P11PROV_CFG_CACHE_SESSIONS, + P11PROV_CFG_ENCODE_PK11_URI_TO_PEM, P11PROV_CFG_SIZE, }; static struct p11prov_cfg_names { const char *name; } p11prov_cfg_names[P11PROV_CFG_SIZE] = { - { "pkcs11-module-path" }, { "pkcs11-module-init-args" }, - { "pkcs11-module-token-pin" }, { "pkcs11-module-allow-export" }, - { "pkcs11-module-login-behavior" }, { "pkcs11-module-load-behavior" }, - { "pkcs11-module-cache-pins" }, { "pkcs11-module-cache-keys" }, - { "pkcs11-module-quirks" }, { "pkcs11-module-cache-sessions" }, + { "pkcs11-module-path" }, + { "pkcs11-module-init-args" }, + { "pkcs11-module-token-pin" }, + { "pkcs11-module-allow-export" }, + { "pkcs11-module-login-behavior" }, + { "pkcs11-module-load-behavior" }, + { "pkcs11-module-cache-pins" }, + { "pkcs11-module-cache-keys" }, + { "pkcs11-module-quirks" }, + { "pkcs11-module-cache-sessions" }, + { "pkcs11-module-encode-key-uri-to-pem" }, }; int OSSL_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH *in, @@ -1513,6 +1545,15 @@ int OSSL_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH *in, } P11PROV_debug("Cache Sessions: %d", ctx->cache_sessions); + if (cfg[P11PROV_CFG_ENCODE_PK11_URI_TO_PEM] != NULL + && strcmp(cfg[P11PROV_CFG_ENCODE_PK11_URI_TO_PEM], "true") == 0) { + ctx->encode_pkey_as_pk11_uri = true; + } else { + ctx->encode_pkey_as_pk11_uri = false; + } + P11PROV_debug("PK11-URI will %sbe written instead of PrivateKeyInfo", + ctx->encode_pkey_as_pk11_uri ? "" : "not "); + /* PAY ATTENTION: do this as the last thing */ if (cfg[P11PROV_CFG_LOAD_BEHAVIOR] != NULL && strcmp(cfg[P11PROV_CFG_LOAD_BEHAVIOR], "early") == 0) { diff --git a/src/provider.h b/src/provider.h index 85829598..40d8575e 100644 --- a/src/provider.h +++ b/src/provider.h @@ -57,6 +57,8 @@ #define P11PROV_DESCS_ED448 "PKCS11 ED448 Implementation" #define P11PROV_NAMES_RAND "PKCS11-RAND" #define P11PROV_DESCS_RAND "PKCS11 Random Generator" +#define P11PROV_NAMES_RSAKEY "RSA" +#define P11PROV_DESCS_RSAKEY "PKCS11 RSA Key" #define P11PROV_PARAM_URI "pkcs11_uri" #define P11PROV_PARAM_KEY_USAGE "pkcs11_key_usage" @@ -155,6 +157,7 @@ int p11prov_pop_error_to_mark(P11PROV_CTX *ctx); #include "session.h" #include "slot.h" #include "random.h" +#include "pk11_uri.h" /* TLS */ int tls_group_capabilities(OSSL_CALLBACK *cb, void *arg); diff --git a/tests/Makefile.am b/tests/Makefile.am index 83a58b90..acdb4662 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -67,7 +67,7 @@ dist_check_SCRIPTS = \ helpers.sh setup-softhsm.sh setup-softokn.sh softhsm-proxy.sh \ test-wrapper tbasic tcerts tecc tecdh tedwards tdemoca thkdf \ toaepsha2 trsapss tdigest ttls tpubkey tfork turi trand tecxc \ - tcms top_state + tcms top_state tpem_encoder test_LIST = \ basic-softokn.t basic-softhsm.t \ @@ -90,7 +90,8 @@ test_LIST = \ uri-softokn.t uri-softhsm.t \ ecxc-softhsm.t \ cms-softokn.t \ - op_state-softhsm.t + op_state-softhsm.t \ + pem_encoder-softokn.t pem_encoder-softhsm.t .PHONY: $(test_LIST) diff --git a/tests/openssl.cnf.in b/tests/openssl.cnf.in index f36469ae..d967f6af 100644 --- a/tests/openssl.cnf.in +++ b/tests/openssl.cnf.in @@ -24,6 +24,7 @@ activate = 1 module = @libtoollibs@/pkcs11@SHARED_EXT@ pkcs11-module-init-args = configDir=@testsblddir@/tmp.softokn/tokens pkcs11-module-token-pin = file:@testsblddir@/pinfile.txt +#pkcs11-module-encode-key-uri-to-pem #pkcs11-module-allow-export #pkcs11-module-load-behavior ##QUIRKS diff --git a/tests/tpem_encoder b/tests/tpem_encoder new file mode 100755 index 00000000..090c7875 --- /dev/null +++ b/tests/tpem_encoder @@ -0,0 +1,110 @@ +#!/bin/bash -e +# Copyright (C) 2022 Simo Sorce +# SPDX-License-Identifier: Apache-2.0 + +source "${TESTSSRCDIR}/helpers.sh" + +# We need to configure early loading otherwise no digests are loaded, +# and all checks are skipped +sed -e "s/#pkcs11-module-encode-key-uri-to-pem/pkcs11-module-encode-key-uri-to-pem = true/" \ + -e "s/#pkcs11-module-load-behavior/pkcs11-module-load-behavior = early/" \ + "${OPENSSL_CONF}" > "${OPENSSL_CONF}.encode_to_pem" +OPENSSL_CONF=${OPENSSL_CONF}.encode_to_pem + +make-pkey-pem() { + URI=$1 + OUT=$2 + + RANDOM_HEX=$(od -A n -N 15 -t x1 /dev/random) + TMP_FILE="${TMPPDIR}/pem-encoder-${RANDOM_HEX}.cnf" + cat > "${TMP_FILE}" << EOF +asn1=SEQUENCE:pk11-uri +[pk11-uri] +type=OID:2.5.4.83 +uri=UTF8:${URI} +EOF + { + echo "-----BEGIN PRIVATE KEY PK11-URI-----" + $CHECKER openssl base64 -in <( $CHECKER openssl asn1parse -noout -out - -genconf "${TMP_FILE}" ) + echo "-----END PRIVATE KEY PK11-URI-----" + } > "${OUT}" + rm "${TMP_FILE}" +} + +sign-verify() { + PRIV_KEY=$1 + PUB_KEY=$2 + FILE=$3 + + RANDOM_HEX=$(od -A n -N 15 -t x1 /dev/random) + TMP_FILE="${TMPPDIR}/sign-verify-pem-encoder-${RANDOM_HEX}.bin" + + $CHECKER openssl pkeyutl -sign -rawin -digest sha256 \ + -inkey "${PRIV_KEY}" \ + -in "${FILE}" \ + -out "${TMP_FILE}" + + $CHECKER openssl pkeyutl -verify -rawin -digest sha256 \ + -inkey "${PUB_KEY}" \ + -pubin \ + -in "${FILE}" \ + -sigfile "${TMP_FILE}" + + rm "${TMP_FILE}" +} + +title PARA "Test PEM Encoding RSA support" + +make-pkey-pem "${PRIURI}" "${TMPPDIR}/priuri-pkey.pem" +sign-verify "${TMPPDIR}/priuri-pkey.pem" "${PUBURI}" "${TMPPDIR}/64krandom.bin" + +RANDOM_HEX=$(od -A n -N 15 -t x1 /dev/random) +export LABEL_SUFFIX_URI=${RANDOM_HEX// /} + +export ALGORITHM=rsa +export ALGORITHM_OPT=rsa_keygen_bits:2048 +ossl ' +genpkey -propquery "provider=pkcs11" + -algorithm "${ALGORITHM}" -pkeyopt "${ALGORITHM_OPT}" + -pkeyopt "pkcs11_uri:pkcs11:object=Test-PEM-Encode-RSA-${LABEL_SUFFIX_URI}" + -out "${TMPPDIR}/rsa-pkey-uri.pem"' + +grep -e "-----BEGIN PRIVATE KEY PK11-URI-----" "${TMPPDIR}/rsa-pkey-uri.pem" + +sign-verify "${TMPPDIR}/rsa-pkey-uri.pem" \ + "pkcs11:object=Test-PEM-Encode-RSA-${LABEL_SUFFIX_URI}" \ + "${TMPPDIR}/64krandom.bin" + + +title PARA "Test PEM Encoding EC support" + +make-pkey-pem "${ECPRIURI}" "${TMPPDIR}/ecpriuri-pkey.pem" +sign-verify "${TMPPDIR}/ecpriuri-pkey.pem" "${ECPUBURI}" "${TMPPDIR}/64krandom.bin" + +export ALGORITHM=EC +export ALGORITHM_OPT=ec_paramgen_curve:prime256v1 +ossl ' +genpkey -propquery "provider=pkcs11" + -algorithm "${ALGORITHM}" -pkeyopt "${ALGORITHM_OPT}" + -pkeyopt "pkcs11_uri:pkcs11:object=Test-PEM-Encode-EC-${LABEL_SUFFIX_URI}" + -out "${TMPPDIR}/ec-pkey-uri.pem"' + +grep -e "-----BEGIN PRIVATE KEY PK11-URI-----" "${TMPPDIR}/ec-pkey-uri.pem" + +sign-verify "${TMPPDIR}/ec-pkey-uri.pem" \ + "pkcs11:object=Test-PEM-Encode-EC-${LABEL_SUFFIX_URI}" \ + "${TMPPDIR}/64krandom.bin" + + +title PARA "Test ambiguous key is unusable" + +make-pkey-pem "${BASEURI}" "${TMPPDIR}/priuri-pkey.pem" +FAIL=0 +ossl ' +pkey -in "${TMPPDIR}/priuri-pkey.pem"' || FAIL=1 +if [ $FAIL -eq 0 ]; then + echo "Should fail because the pem references multiple and/or non-private keys" + exit 1 +fi + +exit 0