// D. J. Bernstein // 20240313 // SPDX-License-Identifier: LicenseRef-PD-hp OR CC0-1.0 OR 0BSD OR MIT-0 OR MIT #define provider_name "openssl_x25519_lib25519" #define provider_version "20240313" /* Basic requirements on the underlying DH library: - Must follow a SUPERCOP-style DH interface. Overall structure of this file: dhlib glue code at the top, then the painful part. The dhlib glue code wraps the DH library: - Sizes: - DHLIB_PKBYTES is the number of public-key bytes. - DHLIB_SKBYTES is the number of secret-key bytes. - DHLIB_SHAREDBYTES is the number of shared-secret bytes. - Data structure: - struct dhlib_key stores data[DHLIB_SKBYTES+DHLIB_PKBYTES], state. - state DHLIB_KEY_EMPTY: data is all 0. - state DHLIB_KEY_PK: data is zeros followed by public key. - state DHLIB_KEY_SK: data is secret key followed by public key. - DHLIB_KEY_EMPTY is guaranteed to be 0. - Functions: - dhlib_key_generate(&K): stores a new secret key in K. - dhlib_key_derive(shared,&peer,&K): derives secret shared with peer. - OpenSSL-style return values: 1 for success, 0 for failure. - Constants to interface with OpenSSL: - dhlib_pubkey_asn1_prefix is DER prefix for public keys - dhlib_privkey_asn1_prefix is DER prefix for secret keys - dhlib_algorithm_name is main command-line name for the system - dhlib_algorithm_names is name:altname1:altname2:... (see openssl/providers/implementations/include/prov/names.h) */ #include // the wrapped library #define dhlib_algorithm_name "X25519" #define dhlib_algorithm_names "X25519:1.3.101.110" static const unsigned char dhlib_pubkey_asn1_prefix[12] = { 0x30,0x2a, 0x30,0x05,0x06,0x03,0x2b,0x65,0x6e, 0x03,0x21,0x00 } ; static const unsigned char dhlib_privkey_asn1_prefix[16] = { 0x30,0x4e, 0x02,0x01,0x00, 0x30,0x05,0x06,0x03,0x2b,0x65,0x6e, 0x04,0x42,0x04,0x40 } ; #define DHLIB_PKBYTES lib25519_dh_PUBLICKEYBYTES #define DHLIB_SKBYTES lib25519_dh_SECRETKEYBYTES #define DHLIB_SHAREDBYTES lib25519_dh_BYTES struct dhlib_key { unsigned char data[DHLIB_SKBYTES+DHLIB_PKBYTES]; unsigned char state; } ; #define DHLIB_KEY_EMPTY 0 #define DHLIB_KEY_PK 1 #define DHLIB_KEY_SK 2 static int dhlib_key_generate(struct dhlib_key *K) { if (!K) return 0; lib25519_dh_keypair(K->data+DHLIB_SKBYTES,K->data); K->state = DHLIB_KEY_SK; return 1; } static int dhlib_key_derive(unsigned char *shared,const struct dhlib_key *peer,const struct dhlib_key *K) { if (!K) return 0; if (K->state != DHLIB_KEY_SK) return 0; if (!peer) return 0; if (!peer->state) return 0; if (!shared) return 0; lib25519_dh(shared,peer->data+DHLIB_SKBYTES,K->data); return 1; } // ----- and now the painful part begins #include #include #include #include #include #include #include #include #include #include #include // various objects below have no parameters static const OSSL_PARAM no_params[] = { OSSL_PARAM_END } ; #define PROVIDER "provider=" provider_name // ----- encoder and decoder // no reason to allocate an extra layer // so pointer to encoder/decoder context is just provider-context pointer static void *coder_newctx(void *provctx) { return provctx; } static void coder_freectx(void *ctx) { ; // nothing to do } static int encode(void *ctx,OSSL_CORE_BIO *out,const void *obj_raw,const OSSL_PARAM obj_abstract[],int selection,OSSL_PASSPHRASE_CALLBACK *cb,void *cbarg,int flagpem) { // XXX: is it possible to skip the PEM part of the encoder? OSSL_LIB_CTX *provider_context = ctx; const struct dhlib_key *K = obj_raw; BIO *bio = 0; int ok = 0; if (obj_abstract) return 0; if (!K) return 0; bio = BIO_new_from_core_bio(provider_context,out); if (!bio) return 0; do { if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { long derlen = (sizeof dhlib_privkey_asn1_prefix)+DHLIB_SKBYTES+DHLIB_PKBYTES; unsigned char *der = OPENSSL_secure_zalloc(derlen); if (!der) break; do { if (K->state != DHLIB_KEY_SK) break; memcpy(der,dhlib_privkey_asn1_prefix,sizeof dhlib_privkey_asn1_prefix); memcpy(der+sizeof dhlib_privkey_asn1_prefix,K->data,DHLIB_SKBYTES+DHLIB_PKBYTES); if (flagpem) { if (PEM_write_bio(bio,"PRIVATE KEY",0,der,derlen) <= 0) break; } else { if (BIO_write(bio,der,derlen) < derlen) break; } ok = 1; } while(0); OPENSSL_secure_clear_free(der,derlen); break; } if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) { long derlen = (sizeof dhlib_pubkey_asn1_prefix)+DHLIB_PKBYTES; unsigned char *der = OPENSSL_zalloc(derlen); if (!der) break; do { if (!K->state) break; memcpy(der,dhlib_pubkey_asn1_prefix,sizeof dhlib_pubkey_asn1_prefix); memcpy(der+sizeof dhlib_pubkey_asn1_prefix,K->data+DHLIB_SKBYTES,DHLIB_PKBYTES); if (flagpem) { if (PEM_write_bio(bio,"PUBLIC KEY",0,der,derlen) <= 0) break; } else { if (BIO_write(bio,der,derlen) < derlen) break; } ok = 1; } while(0); OPENSSL_clear_free(der,derlen); break; } } while(0); BIO_free(bio); return ok; } static int encode_DER(void *ctx,OSSL_CORE_BIO *out,const void *obj_raw,const OSSL_PARAM obj_abstract[],int selection,OSSL_PASSPHRASE_CALLBACK *cb,void *cbarg) { return encode(ctx,out,obj_raw,obj_abstract,selection,cb,cbarg,0); } static int encode_PEM(void *ctx,OSSL_CORE_BIO *out,const void *obj_raw,const OSSL_PARAM obj_abstract[],int selection,OSSL_PASSPHRASE_CALLBACK *cb,void *cbarg) { return encode(ctx,out,obj_raw,obj_abstract,selection,cb,cbarg,1); } static int decode_DER(void *ctx,OSSL_CORE_BIO *in,int selection,OSSL_CALLBACK *data_cb,void *data_cbarg,OSSL_PASSPHRASE_CALLBACK *cb,void *cbarg) { OSSL_LIB_CTX *provider_context = ctx; BIO *bio = 0; int ok = 1; if (!in) return 1; bio = BIO_new_from_core_bio(provider_context,in); if (!bio) return 1; do { int flagpub; for (flagpub = 0;flagpub < 2;++flagpub) { struct dhlib_key *K = 0; const unsigned char *prefix = 0; long prefixlen = 0; long pos = 0; if (!data_cb) break; if (flagpub) { if (!(selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) continue; prefix = dhlib_pubkey_asn1_prefix; prefixlen = sizeof dhlib_pubkey_asn1_prefix; } else { if (!(selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) continue; prefix = dhlib_privkey_asn1_prefix; prefixlen = sizeof dhlib_privkey_asn1_prefix; } for (pos = 0;pos < prefixlen;++pos) { unsigned char ch; if (BIO_read(bio,&ch,1) < 1) break; if (ch != prefix[pos]) break; } if (pos < prefixlen) continue; K = OPENSSL_secure_zalloc(sizeof(struct dhlib_key)); if (!K) continue; do { int pkey = OSSL_OBJECT_PKEY; OSSL_PARAM params[4]; K->state = flagpub ? DHLIB_KEY_PK : DHLIB_KEY_SK; pos = flagpub ? DHLIB_SKBYTES : 0; for (;pos < DHLIB_SKBYTES+DHLIB_PKBYTES;++pos) if (BIO_read(bio,&K->data[pos],1) < 1) break; if (pos < DHLIB_SKBYTES+DHLIB_PKBYTES) break; params[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE,&pkey); params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,dhlib_algorithm_name,0); params[2] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,&K,sizeof(K)); params[3] = OSSL_PARAM_construct_end(); if (data_cb) ok = data_cb(params,data_cbarg); data_cb = 0; // make sure we do not call it twice } while(0); // K should be 0 if and only if callback took control of key if (K) OPENSSL_secure_clear_free(K,sizeof(struct dhlib_key)); } } while(0); BIO_free(bio); return ok; } static const OSSL_DISPATCH encode_DER_functions[] = { { OSSL_FUNC_ENCODER_NEWCTX, (void(*)(void)) coder_newctx }, { OSSL_FUNC_ENCODER_FREECTX, (void(*)(void)) coder_freectx }, { OSSL_FUNC_ENCODER_ENCODE, (void(*)(void)) encode_DER }, { 0, 0 }, } ; static const OSSL_DISPATCH encode_PEM_functions[] = { { OSSL_FUNC_ENCODER_NEWCTX, (void(*)(void)) coder_newctx }, { OSSL_FUNC_ENCODER_FREECTX, (void(*)(void)) coder_freectx }, { OSSL_FUNC_ENCODER_ENCODE, (void(*)(void)) encode_PEM }, { 0, 0 }, } ; static const OSSL_DISPATCH decode_DER_functions[] = { { OSSL_FUNC_DECODER_NEWCTX, (void(*)(void)) coder_newctx }, { OSSL_FUNC_DECODER_FREECTX, (void(*)(void)) coder_freectx }, { OSSL_FUNC_DECODER_DECODE, (void(*)(void)) decode_DER }, { 0, 0 }, } ; static const OSSL_ALGORITHM provider_encoder[] = { { dhlib_algorithm_names,PROVIDER ",output=der,structure=PrivateKeyInfo",encode_DER_functions }, { dhlib_algorithm_names,PROVIDER ",output=pem,structure=PrivateKeyInfo",encode_PEM_functions }, { dhlib_algorithm_names,PROVIDER ",output=der,structure=SubjectPublicKeyInfo",encode_DER_functions }, { dhlib_algorithm_names,PROVIDER ",output=pem,structure=SubjectPublicKeyInfo",encode_PEM_functions }, { 0, 0, 0 }, }; static const OSSL_ALGORITHM provider_decoder[] = { { dhlib_algorithm_names,PROVIDER ",input=der,structure=PrivateKeyInfo",decode_DER_functions }, { dhlib_algorithm_names,PROVIDER ",input=der,structure=SubjectPublicKeyInfo",decode_DER_functions }, { 0, 0, 0 }, }; // ----- keygen // as in encoder/decoder, no reason to allocate an extra layer // so pointer to key-generator context is just provider-context pointer static void *key_gen_init(void *provctx,int selection,const OSSL_PARAM params[]) { return provctx; } static void key_gen_cleanup(void *genctx) { ; // nothing to do } static void *key_new(void *provctx) { return OPENSSL_secure_zalloc(sizeof(struct dhlib_key)); } static void key_free(void *keydata) { OPENSSL_secure_clear_free(keydata,sizeof(struct dhlib_key)); } static int key_gen_set_params(void *genctx,const OSSL_PARAM params[]) { return 1; } static const OSSL_PARAM *key_gen_settable_params(void *genctx,void *provctx) { return no_params; } static void *key_gen(void *genctx,OSSL_CALLBACK *cb,void *cbarg) { struct dhlib_key *K = key_new(genctx); if (!K) return 0; if (!dhlib_key_generate(K)) { key_free(K); return 0; } return K; } static void *key_load(const void *reference,size_t reference_sz) { struct dhlib_key *K = 0; if (!reference) return 0; if (reference_sz != sizeof(K)) return 0; K = *(struct dhlib_key **) reference; *(struct dhlib_key **) reference = 0; return K; } static const OSSL_PARAM keygen_params[] = { OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_MAX_SIZE,0,0), OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST,0,0), OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST,0,0), OSSL_PARAM_END } ; static const OSSL_PARAM *key_gettable_params(void *provctx) { return keygen_params; } static int key_get_params(void *keydata,OSSL_PARAM params[]) { OSSL_PARAM *p = 0; // need MAX_SIZE for openssl pkeyutl -verify p = OSSL_PARAM_locate(params,OSSL_PKEY_PARAM_MAX_SIZE); if (p && !OSSL_PARAM_set_int(p,DHLIB_SHAREDBYTES)) return 0; p = OSSL_PARAM_locate(params,OSSL_PKEY_PARAM_DEFAULT_DIGEST); if (p && !OSSL_PARAM_set_utf8_string(p,SN_undef)) return 0; p = OSSL_PARAM_locate(params,OSSL_PKEY_PARAM_MANDATORY_DIGEST); if (p && !OSSL_PARAM_set_utf8_string(p,SN_undef)) return 0; return 1; } static const OSSL_PARAM *key_settable_params(void *provctx) { return no_params; } static int key_set_params(void *keydata, const OSSL_PARAM params[]) { return 1; } static int key_has(const void *keydata,int selection) { const struct dhlib_key *K = keydata; int result = 1; if (!K) return 0; if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) result &= (K->state != 0); if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) result &= (K->state == DHLIB_KEY_SK); return result; } static int key_match(const void *keydata1,const void *keydata2,int selection) { const struct dhlib_key *K1 = keydata1; const struct dhlib_key *K2 = keydata2; if (!K1) return 0; if (!K2) return 0; if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) if (K1->state && K2->state) if (!CRYPTO_memcmp(K1->data+DHLIB_SKBYTES,K2->data+DHLIB_SKBYTES,DHLIB_PKBYTES)) return 1; if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) if ((K1->state == DHLIB_KEY_SK) && (K2->state == DHLIB_KEY_SK)) if (!CRYPTO_memcmp(K1->data,K2->data,DHLIB_SKBYTES)) return 1; return 0; } static int key_import(void *keydata,int selection,const OSSL_PARAM params[]) { struct dhlib_key *K = keydata; if (!K) return 0; if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) { const OSSL_PARAM *p = OSSL_PARAM_locate_const(params,OSSL_PKEY_PARAM_PUB_KEY); if (p) { size_t len = 0; void *ptr = K->data+DHLIB_SKBYTES; memset(K,0,sizeof(struct dhlib_key)); if (!OSSL_PARAM_get_octet_string(p,&ptr,DHLIB_PKBYTES,&len)) return 0; if (len != DHLIB_PKBYTES) return 0; K->state = DHLIB_KEY_PK; } } if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { const OSSL_PARAM *p = OSSL_PARAM_locate_const(params,OSSL_PKEY_PARAM_PRIV_KEY); if (p) { size_t len = 0; void *ptr = K->data; memset(K,0,sizeof(struct dhlib_key)); if (!OSSL_PARAM_get_octet_string(p,&ptr,DHLIB_SKBYTES+DHLIB_PKBYTES,&len)) return 0; if (len != DHLIB_SKBYTES) return 0; K->state = DHLIB_KEY_SK; } } return 1; } static int key_export(void *keydata,int selection,OSSL_CALLBACK *param_cb,void *cbarg) { struct dhlib_key *K = keydata; #define KEY_EXPORT_PARAMS_MAX 3 OSSL_PARAM params[KEY_EXPORT_PARAMS_MAX]; int paramspos = 0; if (!K) return 0; if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) if (K->state) { if (paramspos >= KEY_EXPORT_PARAMS_MAX) return 0; params[paramspos++] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,K->data+DHLIB_SKBYTES,DHLIB_PKBYTES); } if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) if (K->state == DHLIB_KEY_SK) { if (paramspos >= KEY_EXPORT_PARAMS_MAX) return 0; params[paramspos++] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PRIV_KEY,K->data,DHLIB_SKBYTES); } if (paramspos >= KEY_EXPORT_PARAMS_MAX) return 0; params[paramspos++] = OSSL_PARAM_construct_end(); return param_cb(params,cbarg); } static const OSSL_PARAM params_pub_and_priv[] = { OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY,0,0), OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY,0,0), OSSL_PARAM_END } ; static const OSSL_PARAM *key_import_export_types(int selection) { if (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) return params_pub_and_priv; // maybe in principle should narrow further by pub-vs-priv selection // but examples in openssl do not do that return 0; } static const OSSL_DISPATCH key_functions[] = { { OSSL_FUNC_KEYMGMT_NEW, (void(*)(void)) key_new }, { OSSL_FUNC_KEYMGMT_FREE, (void(*)(void)) key_free }, { OSSL_FUNC_KEYMGMT_GEN_INIT, (void(*)(void)) key_gen_init }, { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void(*)(void)) key_gen_set_params }, { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (void(*)(void)) key_gen_settable_params }, { OSSL_FUNC_KEYMGMT_GEN, (void(*)(void)) key_gen }, { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void(*)(void)) key_gen_cleanup }, { OSSL_FUNC_KEYMGMT_LOAD, (void(*)(void)) key_load }, { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void(*)(void)) key_gettable_params }, { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void(*)(void)) key_get_params }, { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void(*)(void)) key_settable_params }, { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void(*)(void)) key_set_params }, { OSSL_FUNC_KEYMGMT_HAS, (void(*)(void)) key_has }, { OSSL_FUNC_KEYMGMT_MATCH, (void(*)(void)) key_match }, { OSSL_FUNC_KEYMGMT_IMPORT, (void(*)(void)) key_import }, { OSSL_FUNC_KEYMGMT_EXPORT, (void(*)(void)) key_export }, { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void(*)(void)) key_import_export_types }, { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void(*)(void)) key_import_export_types }, { 0, 0 }, } ; static const OSSL_ALGORITHM provider_keygen[] = { { dhlib_algorithm_names,PROVIDER,key_functions }, { 0, 0, 0 }, }; // ----- shared-secret derivation struct dh_context { struct dhlib_key peer; struct dhlib_key key; } ; static void *dh_new(void *provctx) { return OPENSSL_secure_zalloc(sizeof(struct dh_context)); } static void dh_free(void *ctx) { OPENSSL_secure_clear_free(ctx,sizeof(struct dh_context)); } static int dh_copykey(void *ctx,void *provkey,const OSSL_PARAM params[]) { struct dh_context *D = ctx; if (!D) return 0; if (!provkey) return 1; // XXX: where is it documented that provkey can legitimately be 0? // this occurs in "reinit" during, e.g., openssl speed memcpy(&D->key,provkey,sizeof(struct dhlib_key)); return 1; } static int dh_copypeer(void *ctx,void *provkey) { struct dh_context *D = ctx; if (!D) return 0; if (!provkey) return 1; memcpy(&D->peer,provkey,sizeof(struct dhlib_key)); return 1; } static int dh_derive(void *ctx,unsigned char *secret,size_t *secretlen,size_t outlen) { struct dh_context *D = ctx; if (!D) return 0; if (!secret) { *secretlen = DHLIB_SHAREDBYTES; return 1; } if (outlen < DHLIB_SHAREDBYTES) return 0; if (!dhlib_key_derive(secret,&D->peer,&D->key)) return 0; *secretlen = DHLIB_SHAREDBYTES; return 1; } static const OSSL_PARAM *dh_gettable_params(void *provctx) { return no_params; } static int dh_get_params(void *ctx,OSSL_PARAM params[]) { return 1; } static const OSSL_DISPATCH dh_functions[] = { { OSSL_FUNC_KEYEXCH_NEWCTX, (void(*)(void)) dh_new }, { OSSL_FUNC_KEYEXCH_FREECTX, (void(*)(void)) dh_free }, { OSSL_FUNC_KEYEXCH_INIT, (void(*)(void)) dh_copykey }, { OSSL_FUNC_KEYEXCH_SET_PEER, (void(*)(void)) dh_copypeer }, { OSSL_FUNC_KEYEXCH_DERIVE, (void(*)(void)) dh_derive }, { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, (void(*)(void)) dh_gettable_params }, { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void(*)(void)) dh_get_params }, { 0, 0 }, }; static const OSSL_ALGORITHM provider_dh[] = { { dhlib_algorithm_names,PROVIDER,dh_functions }, { 0, 0, 0 }, }; // ----- top-level provider functions static const OSSL_ALGORITHM *provider_get_algorithms(void *provctx,int operation_id,int *no_store) { *no_store = 0; // ok for openssl to store results if (operation_id == OSSL_OP_ENCODER) return provider_encoder; if (operation_id == OSSL_OP_DECODER) return provider_decoder; if (operation_id == OSSL_OP_KEYMGMT) return provider_keygen; if (operation_id == OSSL_OP_KEYEXCH) return provider_dh; return 0; } static const OSSL_PARAM provider_params_list[] = { OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME,OSSL_PARAM_UTF8_PTR,0,0), OSSL_PARAM_DEFN(OSSL_PROV_PARAM_VERSION,OSSL_PARAM_UTF8_PTR,0,0), OSSL_PARAM_DEFN(OSSL_PROV_PARAM_STATUS,OSSL_PARAM_INTEGER,0,0), OSSL_PARAM_END, } ; static const OSSL_PARAM *provider_gettable_params(void) { return provider_params_list; } // used by, e.g., openssl list -providers static int provider_get_params(void *provctx,OSSL_PARAM params[]) { OSSL_PARAM *p = 0; p = OSSL_PARAM_locate(params,OSSL_PROV_PARAM_NAME); if (p && !OSSL_PARAM_set_utf8_ptr(p,provider_name)) return 0; p = OSSL_PARAM_locate(params,OSSL_PROV_PARAM_VERSION); if (p && !OSSL_PARAM_set_utf8_ptr(p,provider_version)) return 0; p = OSSL_PARAM_locate(params,OSSL_PROV_PARAM_STATUS); if (p && !OSSL_PARAM_set_int(p,1)) return 0; // 1 means provider is running return 1; } static void provider_teardown(void *provctx) { OSSL_LIB_CTX_free(provctx); } static const OSSL_DISPATCH provider_functions[] = { { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void(*)(void)) provider_get_algorithms }, { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void(*)(void)) provider_gettable_params }, { OSSL_FUNC_PROVIDER_GET_PARAMS, (void(*)(void)) provider_get_params }, { OSSL_FUNC_PROVIDER_TEARDOWN, (void(*)(void)) provider_teardown }, { 0, 0 }, }; int OSSL_provider_init(const OSSL_CORE_HANDLE *handle,const OSSL_DISPATCH *in,const OSSL_DISPATCH **out,void **provctx) { OSSL_LIB_CTX *provider_context = OSSL_LIB_CTX_new_child(handle,in); if (!provider_context) return 0; *out = provider_functions; *provctx = provider_context; return 1; }