diff options
author | James Bottomley <James.Bottomley@HansenPartnership.com> | 2023-02-22 10:55:45 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2023-03-22 22:59:44 -0400 |
commit | 44706619a76f5d5092b4d2f193f277bb4393a59e (patch) | |
tree | 45e237f03a271d9fffd795daeee4faf75efb0244 | |
parent | 97543e17c5bcb06006495099f190b9421c9a2016 (diff) | |
download | openssl_tpm2_engine-44706619a76f5d5092b4d2f193f277bb4393a59e.tar.gz |
Add openssl3 provider
Basic provider with decoders, keymanagement and signatures
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | src/include/tpm2-common.h | 1 | ||||
-rw-r--r-- | src/provider/Makefile.am | 16 | ||||
-rw-r--r-- | src/provider/decode_encode.c | 194 | ||||
-rw-r--r-- | src/provider/keymgmt.c | 158 | ||||
-rw-r--r-- | src/provider/provider.c | 141 | ||||
-rw-r--r-- | src/provider/provider.h | 34 |
8 files changed, 553 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 4e0e7f3..87ed76c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,4 +5,5 @@ SUBDIRS += src/libcommon SUBDIRS += src/engine SUBDIRS += src/tools SUBDIRS += src/opensslmissing +SUBDIRS += src/provider SUBDIRS += tests/engine diff --git a/configure.ac b/configure.ac index 241fd1f..c3e0291 100644 --- a/configure.ac +++ b/configure.ac @@ -82,6 +82,11 @@ if test -z "$enginesdir" ; then AC_MSG_FAILURE([Failed to find SSL engines directory]) fi +if test "$ac_have_openssl3" = "1"; then + modulesdir=\"`$PKG_CONFIG --variable=modulesdir libcrypto`\" + AC_SUBST(modulesdir) +fi + AC_SUBST(enginesdir) PKG_CHECK_MODULES([DEPS], [libcrypto]) @@ -166,12 +171,14 @@ AC_OUTPUT([Makefile src/libcommon/Makefile src/opensslmissing/Makefile src/engine/Makefile - src/tools/Makefile]) + src/tools/Makefile + src/provider/Makefile]) cat <<EOF CFLAGS: ${CFLAGS} openssl engines directory: ${enginesdir} +openssl provider directory: ${modulesdir} swtpm for testing: ${testtpm} tss libraries: ${tsslibs} diff --git a/src/include/tpm2-common.h b/src/include/tpm2-common.h index 610750b..39a5036 100644 --- a/src/include/tpm2-common.h +++ b/src/include/tpm2-common.h @@ -48,6 +48,7 @@ struct app_data { struct policies *pols; int num_pols; int empty_auth; + _Atomic int refs; ENGINE *e; }; diff --git a/src/provider/Makefile.am b/src/provider/Makefile.am new file mode 100644 index 0000000..6e2b617 --- /dev/null +++ b/src/provider/Makefile.am @@ -0,0 +1,16 @@ +AM_CPPFLAGS=-I../include +COMMONLIB = ../libcommon/libcommon.a ../opensslmissing/libosslm.a + +if HAVE_OPENSSL3 +openssl_provider_LTLIBRARIES=libtpm2.la +openssl_providerdir=@modulesdir@ + +libtpm2_la_LDFLAGS= -no-undefined -avoid-version +libtpm2_la_LIBADD=${COMMONLIB} ${DEPS_LIBS} +libtpm2_la_SOURCES=provider.c decode_encode.c keymgmt.c +libtpm2_la_CFLAGS=${DEPS_CFLAGS} -g -Werror + +install-data-hook: + cd $(DESTDIR)$(openssl_providerdir) && $(LN_S) -f libtpm2@SHREXT@ tpm2@SHREXT@ + +endif diff --git a/src/provider/decode_encode.c b/src/provider/decode_encode.c new file mode 100644 index 0000000..cbd6c7a --- /dev/null +++ b/src/provider/decode_encode.c @@ -0,0 +1,194 @@ +/* Copyright (C) 2023 James Bottomley <James.Bottomley@HansenPartnership.com> + * + * SPDX-License-Identifier: LGPL-2.1-only + */ +#include "provider.h" + +static int tpm2_pem_decode(void *ctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + int ret = 1; /* carry on decoding, even if we can't decode this */ + OSSL_LIB_CTX *libctx = ctx; + BIO *in = BIO_new_from_core_bio(libctx, cin); + unsigned char *der_data; + long der_len; + char *pem_name, *pem_header; + OSSL_PARAM params[3]; + + if (!in) + /* stop decoding */ + return 0; + + if (PEM_read_bio(in, &pem_name, &pem_header, &der_data, &der_len) <= 0) + goto out; + + if (strcmp(pem_name, TSSPRIVKEY_PEM_STRING) != 0 && + strcmp(pem_name, TSSLOADABLE_PEM_STRING) != 0) + goto out; + + 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, + "TPM2", 0); + params[2] = OSSL_PARAM_construct_end(); + ret = data_cb(params, data_cbarg); + + out: + OPENSSL_free(pem_name); + OPENSSL_free(pem_header); + OPENSSL_free(der_data); + BIO_free(in); + + return ret; +} + +static int tpm2_pkey_decode(void *ctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg, + TPM_ALG_ID alg) +{ + struct app_data *ad = OPENSSL_zalloc(sizeof(*ad)); + OSSL_LIB_CTX *libctx = ctx; + + BIO *in = BIO_new_from_core_bio(libctx, cin); + int ret = 0; + OSSL_PARAM params[4]; + int type; + char *keytype; + + if (!ad || !in) { + BIO_free(in); + OPENSSL_free(ad); + return 0; + } + + ret = tpm2_load_bf(in, ad, NULL); + BIO_free(in); + if (!ret) + goto out_free; + + ret = 1; + if (ad->Public.publicArea.type != alg) + goto out_free; + + if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { + if (!ad->empty_auth) { + char pass[SHA512_DIGEST_LENGTH]; + size_t len; + + if (!pw_cb(pass, sizeof(pass), &len, NULL, pw_cbarg)) + goto out_free; + ad->auth = OPENSSL_malloc(len + 1); + if (!ad->auth) + goto out_free; + memcpy(ad->auth, pass, len); + ad->auth[len] = '\0'; + OPENSSL_cleanse(pass, len); + } + } else { + OPENSSL_free(ad->priv); + ad->priv = NULL; + } + + type = OSSL_OBJECT_PKEY; + keytype = alg == TPM_ALG_RSA ? "RSA" : "EC"; + params[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, + &type); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + keytype, 0); + params[2] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE, + &ad, sizeof(ad)); + params[3] = OSSL_PARAM_construct_end(); + + ret = data_cb(params, data_cbarg); + if (ret) + /* here the key must not be freed. It is freed + * instead by keymgmt_free once all references are + * dropped */ + return ret; + + out_free: + tpm2_delete(ad); + return ret; +} + +static int tpm2_rsa_decode(void *ctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + return tpm2_pkey_decode(ctx, cin, selection, data_cb, data_cbarg, + pw_cb, pw_cbarg, TPM_ALG_RSA); +} + +static int tpm2_ec_decode(void *ctx, OSSL_CORE_BIO *cin, int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + return tpm2_pkey_decode(ctx, cin, selection, data_cb, data_cbarg, + pw_cb, pw_cbarg, TPM_ALG_ECC); +} + +static int +tpm2_encode_text(void *ctx, OSSL_CORE_BIO *cout, const void *key, + const OSSL_PARAM key_abstract[], int selection, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + OSSL_LIB_CTX *libctx = ctx; + BIO *out = BIO_new_from_core_bio(libctx, cout); + const struct app_data *ad = key; + + if (!out) + return 0; + + BIO_printf(out, "TPM %s key, parent=%08x\n", + ad->Public.publicArea.type == TPM_ALG_RSA ? "RSA" : "EC", + ad->parent); + + BIO_free(out); + + return 1; +} + +static const OSSL_DISPATCH encode_text_fns[] = { + { OSSL_FUNC_ENCODER_NEWCTX, (void (*)(void))tpm2_passthrough_newctx }, + { OSSL_FUNC_ENCODER_FREECTX, (void (*)(void))tpm2_passthrough_freectx }, + { OSSL_FUNC_ENCODER_ENCODE, (void (*)(void))tpm2_encode_text }, + { 0, NULL } +}; + +static const OSSL_DISPATCH decode_pem_fns[] = { + { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))tpm2_passthrough_newctx }, + { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))tpm2_passthrough_freectx }, + { OSSL_FUNC_DECODER_DECODE, (void (*)(void))tpm2_pem_decode }, + { 0, NULL } +}; + +static const OSSL_DISPATCH decode_rsa_fns[] = { + { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))tpm2_passthrough_newctx }, + { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))tpm2_passthrough_freectx }, + { OSSL_FUNC_DECODER_DECODE, (void (*)(void))tpm2_rsa_decode }, + { 0, NULL } +}; + +static const OSSL_DISPATCH decode_ec_fns[] = { + { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))tpm2_passthrough_newctx }, + { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))tpm2_passthrough_freectx }, + { OSSL_FUNC_DECODER_DECODE, (void (*)(void))tpm2_ec_decode }, + { 0, NULL } +}; + +/* only provide pretty print encoders. All other key saves + * are done by keymgmt export (which means only public keys) */ +const OSSL_ALGORITHM encoders[] = { + { "RSA", "provider=tpm2,output=text", encode_text_fns }, + { "EC", "provider=tpm2,output=text", encode_text_fns }, + { NULL, NULL, NULL } +}; + +const OSSL_ALGORITHM decoders[] = { + { "DER", "provider=tpm2,input=pem", decode_pem_fns }, + { "RSA", "provider=tpm2,input=der,structure=TPM2", decode_rsa_fns }, + { "EC", "provider=tpm2,input=der,structure=TPM2", decode_ec_fns }, + { NULL, NULL, NULL } +}; diff --git a/src/provider/keymgmt.c b/src/provider/keymgmt.c new file mode 100644 index 0000000..d11172c --- /dev/null +++ b/src/provider/keymgmt.c @@ -0,0 +1,158 @@ +/* Copyright (C) 2023 James Bottomley <James.Bottomley@HansenPartnership.com> + * + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/* note: we need a reference in struct app_dir which uses gcc atomics */ +#include <stdatomic.h> + +#include "provider.h" +#include "opensslmissing.h" + +static void *tpm2_keymgmt_load(void *ref, size_t ref_size) +{ + struct app_data *ad; + void **actual_ref = ref; + + ad = *actual_ref; + *actual_ref = NULL; + + atomic_fetch_add_explicit(&ad->refs, 1, memory_order_relaxed); + + return ad; +} + +static void tpm2_keymgmt_free(void *ref) +{ + struct app_data *ad = ref; + int refcnt = atomic_fetch_sub_explicit(&ad->refs, 1, + memory_order_relaxed); + if (refcnt == 1) + tpm2_delete(ad); + if (refcnt < 1) + fprintf(stderr, "keymgmt free wrong reference %d\n", refcnt); +} + +/* another one of openssls never used functions that has to be provided */ +static const OSSL_PARAM *tpm2_keymgmt_gettable_params(void *ctx) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_END + }; + + return params; +} + +static int tpm2_keymgmt_get_params(void *ref, OSSL_PARAM params[]) +{ + struct app_data *ad = ref; + int maxsize, bits, securitybits; + OSSL_PARAM *p; + + if (!tpm2_get_sizes(ad, &bits, &securitybits, &maxsize)) + return 0; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS); + if (p != NULL && !OSSL_PARAM_set_int(p, bits)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS); + if (p != NULL && !OSSL_PARAM_set_int(p, securitybits)) + return 0; + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE); + if (p != NULL && !OSSL_PARAM_set_int(p, maxsize)) + return 0; + return 1; +} + + +static int tpm2_keymgmt_has(const void *ref, int selection) +{ + const struct app_data *ad = ref; + + if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + return 1; + if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) + return ad->priv != NULL; + return 0; +} + +static int tpm2_keymgmt_export(void *ref, int selection, + OSSL_CALLBACK *param_cb, void *cbarg) +{ + OSSL_PARAM params[3], *p = params; + struct app_data *ad = ref; + TPMT_PUBLIC *pub = &ad->Public.publicArea; + unsigned long exp = 0x10001; + int nid, ret; + size_t len; + unsigned char point[MAX_ECC_KEY_BYTES*2 + 1], *pt = point; + unsigned char *n = NULL; + + if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0) + goto out; + if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) + return 0; + + switch (pub->type) { + case TPM_ALG_RSA: + if (!bn_b2h_alloc(&n, VAL_2B(pub->unique.rsa, buffer), + VAL_2B(pub->unique.rsa, size))) + return 0; + + *p++ = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, n, + VAL_2B(pub->unique.rsa, size)); + *p++ = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, + (unsigned char *)&exp, + sizeof(exp)); + break; + case TPM_ALG_ECC: + nid = tpm2_curve_name_to_nid(pub->parameters.eccDetail.curveID); + len = VAL_2B(pub->unique.ecc.x, size) + + VAL_2B(pub->unique.ecc.y, size) + 1; + *pt++ = POINT_CONVERSION_UNCOMPRESSED; + memcpy(pt, VAL_2B(pub->unique.ecc.x, buffer), + VAL_2B(pub->unique.ecc.x, size)); + pt += VAL_2B(pub->unique.ecc.x, size); + memcpy(pt, VAL_2B(pub->unique.ecc.y, buffer), + VAL_2B(pub->unique.ecc.y, size)); + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, + (char *)OBJ_nid2sn(nid), 0); + *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, + point, len); + break; + } + out: + *p = OSSL_PARAM_construct_end(); + + ret = param_cb(params, cbarg); + bn_b2h_free(n); + return ret; +} + +/* this function must be provided but is never used */ +static const OSSL_PARAM *tpm2_keymgmt_export_types(int selection) +{ + return NULL; +} + +static const OSSL_DISPATCH keymgmt_fns[] = { + { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))tpm2_keymgmt_load }, + { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))tpm2_keymgmt_free }, + { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))tpm2_keymgmt_has }, + { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*)(void))tpm2_keymgmt_get_params }, + { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*)(void))tpm2_keymgmt_gettable_params }, + /* both MUST be provided (enforced) although no-one knows why + * since openssl never uses export_types */ + { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))tpm2_keymgmt_export }, + { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))tpm2_keymgmt_export_types }, + { 0, NULL} +}; + +const OSSL_ALGORITHM keymgmts[]= { + { "RSA", "provider=tpm2", keymgmt_fns }, + { "EC", "provider=tpm2", keymgmt_fns }, + { NULL, NULL, NULL} +}; diff --git a/src/provider/provider.c b/src/provider/provider.c new file mode 100644 index 0000000..f4473d1 --- /dev/null +++ b/src/provider/provider.c @@ -0,0 +1,141 @@ +/* Copyright (C) 2023 James Bottomley <James.Bottomley@HansenPartnership.com> + * + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#include <string.h> +#include <stdio.h> +#include <stdarg.h> +#include <openssl/err.h> +#include <openssl/pem.h> +#include <openssl/e_os2.h> +#include "opensslmissing.h" + +#include "provider.h" + +char *srk_auth = NULL; + +static OSSL_FUNC_core_get_params_fn *core_get_params; + + +/* only need the provider context, so pass through */ +void *tpm2_passthrough_newctx(void *ctx) +{ + return ctx; +} + +void tpm2_passthrough_freectx(void *ctx) +{ + /* we don't own the passed through context, so do nothing */ +} + +int tpm2_get_sizes(const struct app_data *ad, int *bits, int *security, + int *size) +{ + const TPMT_PUBLIC *pub = &ad->Public.publicArea; + int b, sb, sz; + + switch (pub->type) { + case TPM_ALG_RSA: + b = pub->parameters.rsaDetail.keyBits; + sz = b/8; + sb = (b == 3072) ? 128 : 112; + break; + case TPM_ALG_ECC: + b = tpm2_curve_to_order(pub->parameters.eccDetail.curveID)*8; + sb = b/2; + /* SEQUENCE ( BIGNUM, BIGNUM ) up to 32k */ + sz = (b/8 + 3)*2 + 3; + break; + default: + return 0; + } + + if (bits) + *bits = b; + if (security) + *security = sb; + if (size) + *size = sz; + + return 1; +} + +#define QOP(op, routine) \ + [op] = { routine, #op, } +static struct { + const OSSL_ALGORITHM *alg; + const char *desc; +} queries[] = { + QOP(OSSL_OP_DECODER, decoders), + QOP(OSSL_OP_ENCODER, encoders), + QOP(OSSL_OP_KEYMGMT, keymgmts), + QOP(OSSL_OP_STORE, NULL), +}; + +static const OSSL_ALGORITHM *p_query(void *provctx, int operation_id, + int *no_store) +{ + return queries[operation_id].alg; +} + +static void p_teardown(void *ctx) +{ + OSSL_LIB_CTX *libctx = ctx; + OSSL_LIB_CTX_free(libctx); +} + +static const OSSL_DISPATCH prov_fns[] = { + { OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))p_teardown }, + { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))p_query }, + { 0, NULL } +}; + +OSSL_provider_init_fn OSSL_provider_init; +int OSSL_provider_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, + void **provctx) +{ + OSSL_LIB_CTX *libctx; + const OSSL_DISPATCH *fns = in; + OSSL_PARAM provider_params[] = { + OSSL_PARAM_utf8_ptr("PIN", &srk_auth, 0), + OSSL_PARAM_END + }; + + + *out = prov_fns; + + for (; fns->function_id != 0; fns++) { + switch (fns->function_id) { + case OSSL_FUNC_CORE_GET_PARAMS: + core_get_params = OSSL_FUNC_core_get_params(fns); + break; + } + } + if (!core_get_params) { + fprintf(stderr, "core didn't provide get_params\n"); + goto err; + } + + if (!core_get_params(handle, provider_params)) { + fprintf(stderr, "core failed to load params\n"); + goto err; + } + + libctx = OSSL_LIB_CTX_new_from_dispatch(handle, in); + if (libctx == NULL) { + fprintf(stderr, "tpm2-provider: Allocation failure in init\n"); + goto err; + } + + fprintf(stderr, "tpm2-provider initialized\n"); + *provctx = libctx; + + return 1; + + err: + p_teardown(libctx); + return 0; +} diff --git a/src/provider/provider.h b/src/provider/provider.h new file mode 100644 index 0000000..73ed211 --- /dev/null +++ b/src/provider/provider.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2023 James Bottomley <James.Bottomley@HansenPartnership.com> + * + * SPDX-License-Identifier: LGPL-2.1-only + */ +#ifndef _PROVIDER_H +#define _PROVIDER_H + +#include <openssl/core.h> +#include <openssl/core_names.h> +#include <openssl/core_dispatch.h> +#include <openssl/core_object.h> +#include <openssl/bio.h> + +#include "tpm2-tss.h" +#include "tpm2-asn.h" +#include "tpm2-common.h" + +extern char *srk_auth; + +/* core context functions in provider.h */ +void *tpm2_passthrough_newctx(void *ctx); +void tpm2_passthrough_freectx(void *ctx); +int tpm2_get_sizes(const struct app_data *ad, int *bits, int *security, + int *size); + +/* decode_encode.c */ +extern const OSSL_ALGORITHM encoders[]; +extern const OSSL_ALGORITHM decoders[]; + +/* keymgmt.c */ + +extern const OSSL_ALGORITHM keymgmts[]; + +#endif |