To give a bit more context: The attached beta openssl_ed25519_lib25519.c is an OpenSSL provider that uses lib25519 to speed up Ed25519. On, e.g., a 3.5GHz Haswell core, openssl speed with OpenSSL's built-in provider says 21239.0 sign/s and 8272.0 verify/s, while openssl speed with this provider says 73708.1 sign/s and 22853.0 verify/s. I used a different secret-key format (and maybe cheated a bit on the OIDs for that) but the public keys are compatible. I'm planning similar X25519 integration. For an idea of the speedups on various Intel/AMD platforms, see https://lib25519.cr.yp.to/speed.html. There's also ARM code coming soon. An important caveat is that the code isn't formally verified yet; but there's some fully verified code from s2n-bignum that drops straightforwardly into lib25519, and reusing that code helps security even for applications that don't care about speed. I haven't dug this deeply into OpenSSL before. I could easily be doing major things wrong in this provider, maybe security-relevant things, perhaps most importantly regarding memory management. The documentation that I looked at had occasional comments on who was responsible for each piece of memory at each moment, but most of the time I found myself running tests and studying code samples (in the OpenSSL core, OpenSSL's default provider, and a few providers I found online) to try to figure out the expectations. Some memory-management issues that I bumped into were easily avoidable. For example, bad memory management in the OpenSSL core, specifically a bad free() for openssl req, occurs if a provider returns bad _bytes_ inside an OSSL_SIGNATURE_PARAM_ALGORITHM_ID octet string. Well, okay, obviously I should get the ID right. (The core should be fixed too: string contents should never be able to corrupt pointer structures.) As another example, I think I understand the layering that's expected to be used inside encoders and decoders, but the layers for public-key decoding were turning into such a data-structure mess that I ended up just tossing that code and doing a trivial parse of the known DER prefix followed by the Ed25519 byte string. It's tempting to simplify the other encoding/decoding steps in the same way. But obviously a provider can't avoid some issues. In particular, there's a three-way dance between decode() and its callback and load(), and I want to make sure that I'm handling memory in the expected way for all possible failure cases. It would be nice if OpenSSL included a test harness for providers---not just basic functionality tests as in the attached test.sh but also testing how various types of internal failures are handled. ---D. J. Bernstein
/* Basic requirements on the underlying signature library: - Must follow a SUPERCOP-style interface. - The secret key must end with the public key. - The signed message must be a signature followed by the message. - Signatures must be constant length. Overall structure of this file: glue code at the top, then the painful part. The glue code wraps the signature library: - Sizes: - PKBYTES is the number of public-key bytes. - SKBYTES is the number of secret-key bytes. - SIGBYTES is the number of signature bytes. - Data structure: - struct glue_key stores data[SKBYTES] and state. - state GLUE_KEY_EMPTY: data is all 0. - state GLUE_KEY_PK: data is zeros followed by public key. - state GLUE_KEY_SK: data is secret key. - GLUE_KEY_EMPTY is guaranteed to be 0. - Functions: - glue_key_generate(&K): stores a new secret key in K. - glue_key_sign(sig,m,mlen,&K): signs a message using K. - glue_key_verify(sig,m,mlen,&K): verifies a signature using K. - OpenSSL-style return values: 1 for success, 0 for failure. - Constants to interface with OpenSSL: - glue_pubkey_asn1_prefix is DER prefix for public keys - glue_algorithm_asn1 is DER encoding of just the system name - glue_algorithm_name is main command-line name for the system - glue_algorithm_names is name:altname1:altname2:... (see openssl/providers/implementations/include/prov/names.h) - glue_algorithm_nid is the OpenSSL NID for the signature system (as NID_...) - glue_provider_name - glue_provider_version */ #include <openssl/crypto.h> // to use OPENSSL_zalloc in the glue code #include <inttypes.h> // for SIZE_MAX, LLONG_MAX #include <string.h> // for memset #include <lib25519.h> // the wrapped library #define glue_provider_name "openssl_ed25519_lib25519" #define glue_provider_version "20240301" #define glue_algorithm_name "ED25519" #define glue_algorithm_names "ED25519:1.3.101.112" #define glue_algorithm_nid NID_ED25519 // XXX: must be correct DER; otherwise triggers double-free in openssl req static const unsigned char glue_algorithm_asn1[7] = { 0x30,0x05,0x06,0x03,0x2b,0x65,0x70, } ; static const unsigned char glue_pubkey_asn1_prefix[12] = { 0x30,0x2a, 0x30,0x05,0x06,0x03,0x2b,0x65,0x70, 0x03,0x21,0x00 } ; #define PKBYTES lib25519_sign_PUBLICKEYBYTES #define SKBYTES lib25519_sign_SECRETKEYBYTES #define SIGBYTES lib25519_sign_BYTES #if PKBYTES > SKBYTES #error PKBYTES has to be at most SKBYTES #endif struct glue_key { unsigned char data[SKBYTES]; unsigned char state; } ; #define GLUE_KEY_EMPTY 0 #define GLUE_KEY_PK 1 #define GLUE_KEY_SK 2 static int glue_key_generate(struct glue_key *K) { unsigned char pk[PKBYTES]; if (!K) return 0; lib25519_sign_keypair(pk,K->data); if (CRYPTO_memcmp(K->data+SKBYTES-PKBYTES,pk,PKBYTES)) { memset(K,0,sizeof(struct glue_key)); return 0; } K->state = GLUE_KEY_SK; return 1; } static int glue_key_sign(unsigned char *sig,const unsigned char *m,size_t mlen,const struct glue_key *K) { int ok = 0; void *sm = 0; long long smlen = 0; long long smlenused = 0; if (!sig) return 0; memset(sig,0,SIGBYTES); if (!K) return 0; if (K->state != GLUE_KEY_SK) return 0; if (!m) return 0; if (mlen > SIZE_MAX-SIGBYTES) return 0; if (mlen > LLONG_MAX-SIGBYTES) return 0; smlen = mlen+SIGBYTES; sm = OPENSSL_secure_zalloc(smlen); if (!sm) return 0; do { lib25519_sign(sm,&smlenused,m,mlen,K->data); if (smlenused != smlen) break; if (CRYPTO_memcmp(m,sm+SIGBYTES,mlen)) break; memcpy(sig,sm,SIGBYTES); ok = 1; } while(0); OPENSSL_secure_clear_free(sm,smlen); return ok; } static int glue_key_verify(const unsigned char *sig,const unsigned char *m,size_t mlen,const struct glue_key *K) { int ok = 0; void *sm = 0; long long smlen = 0; long long tlen = 0; if (!K) return 0; if (!K->state) return 0; if (!m) return 0; if (!sig) return 0; if (mlen > SIZE_MAX-SIGBYTES) return 0; if (mlen > LLONG_MAX-SIGBYTES) return 0; smlen = mlen+SIGBYTES; sm = OPENSSL_zalloc(smlen); if (!sm) return 0; do { memcpy(sm,sig,SIGBYTES); memcpy(sm+SIGBYTES,m,mlen); if (lib25519_sign_open(sm,&tlen,sm,smlen,K->data+SKBYTES-PKBYTES) != 0) break; if ((unsigned long long) tlen != mlen) break; if (CRYPTO_memcmp(sm,m,mlen)) break; ok = 1; } while(0); OPENSSL_clear_free(sm,smlen); return ok; } // ----- and now the painful part begins #include <openssl/core.h> #include <openssl/core_dispatch.h> #include <openssl/core_object.h> #include <openssl/core_names.h> #include <openssl/params.h> #include <openssl/param_build.h> #include <openssl/provider.h> #include <openssl/bio.h> #include <openssl/asn1.h> #include <openssl/pem.h> #include <openssl/x509.h> extern int asn1_d2i_read_bio(BIO *,BUF_MEM **); // XXX: sigh // various objects below have no parameters static const OSSL_PARAM no_params[] = { OSSL_PARAM_END } ; #define PROVIDER "provider=" glue_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: probably easier to just precompute constant strings for all four cases // XXX: is it possible to skip the PEM part of the encoder? OSSL_LIB_CTX *provider_context = ctx; const struct glue_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) { ASN1_OCTET_STRING bytes; unsigned char *der = 0; int derlen; PKCS8_PRIV_KEY_INFO *sk_pkcs8; if (K->state != GLUE_KEY_SK) break; bytes.data = (void *) K->data; bytes.length = SKBYTES; bytes.flags = 0; // XXX: should this be trying to use secure_zalloc instead? derlen = i2d_ASN1_OCTET_STRING(&bytes,&der); if (derlen < 0) break; do { sk_pkcs8 = PKCS8_PRIV_KEY_INFO_new(); if (!sk_pkcs8) break; do { if (!PKCS8_pkey_set0(sk_pkcs8,OBJ_nid2obj(glue_algorithm_nid),0,V_ASN1_UNDEF,0,der,derlen)) break; der = 0; // since sk_pkcs8 has taken responsibility if (flagpem) { if (!PEM_write_bio_PKCS8_PRIV_KEY_INFO(bio,sk_pkcs8)) break; } else { if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio,sk_pkcs8)) break; } ok = 1; } while(0); PKCS8_PRIV_KEY_INFO_free(sk_pkcs8); } while(0); if (der) OPENSSL_clear_free(der,derlen); break; } if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) { unsigned char *pk_copy = OPENSSL_zalloc(PKBYTES); if (!pk_copy) break; do { X509_PUBKEY *pk_x509 = X509_PUBKEY_new(); if (!pk_x509) break; do { if (!K->state) break; memcpy(pk_copy,K->data+SKBYTES-PKBYTES,PKBYTES); if (!X509_PUBKEY_set0_param(pk_x509,OBJ_nid2obj(glue_algorithm_nid),V_ASN1_UNDEF,0,pk_copy,PKBYTES)) break; pk_copy = 0; // since pk_x509 has taken responsibility if (flagpem) { if (!PEM_write_bio_X509_PUBKEY(bio,pk_x509)) break; } else { if (!i2d_X509_PUBKEY_bio(bio,pk_x509)) break; } ok = 1; } while(0); X509_PUBKEY_free(pk_x509); break; } while(0); if (pk_copy) OPENSSL_clear_free(pk_copy,PKBYTES); } } 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) { if (!data_cb) break; if (flagpub) { if (!(selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) continue; } else { if (!(selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) continue; } BUF_MEM *mem = 0; long xlen = 0; const unsigned char *x2 = 0; PKCS8_PRIV_KEY_INFO *sk_pkcs8 = 0; if (asn1_d2i_read_bio(bio,&mem) < 0) continue; x2 = (void *) mem->data; xlen = mem->length; do { if (flagpub) { // maybe could do something like this: pk_x509 = d2i_X509_PUBKEY(0,&x2,xlen); // but clearly easier to just decode by hand if (xlen != PKBYTES + sizeof glue_pubkey_asn1_prefix) break; if (CRYPTO_memcmp(x2,glue_pubkey_asn1_prefix,sizeof glue_pubkey_asn1_prefix)) break; } else { // XXX: consider decoding directly in this case too sk_pkcs8 = d2i_PKCS8_PRIV_KEY_INFO(0,&x2,xlen); if (!sk_pkcs8) break; } do { const unsigned char *y = 0; int ylen = 0; const X509_ALGOR *algorithmid = 0; ASN1_OCTET_STRING *bytes = 0; if (!flagpub) { if (!PKCS8_pkey_get0(0,&y,&ylen,&algorithmid,sk_pkcs8)) break; if (OBJ_obj2nid(algorithmid->algorithm) != glue_algorithm_nid) break; bytes = d2i_ASN1_OCTET_STRING(0,&y,ylen); if (!bytes) break; } do { struct glue_key *K = 0; if (!flagpub) if (ASN1_STRING_length(bytes) != SKBYTES) break; K = OPENSSL_secure_zalloc(sizeof(struct glue_key)); if (!K) break; do { int pkey = OSSL_OBJECT_PKEY; OSSL_PARAM params[4]; if (flagpub) { memcpy(K->data+SKBYTES-PKBYTES,x2+xlen-PKBYTES,PKBYTES); K->state = GLUE_KEY_PK; } else { memcpy(K->data,ASN1_STRING_get0_data(bytes),SKBYTES); K->state = GLUE_KEY_SK; } params[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE,&pkey); params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,glue_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 glue_key)); } while(0); if (!flagpub) ASN1_OCTET_STRING_free(bytes); } while(0); if (!flagpub) PKCS8_PRIV_KEY_INFO_free(sk_pkcs8); } while(0); BUF_MEM_free(mem); } } 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[] = { { glue_algorithm_names,PROVIDER ",output=der,structure=PrivateKeyInfo",encode_DER_functions }, { glue_algorithm_names,PROVIDER ",output=pem,structure=PrivateKeyInfo",encode_PEM_functions }, { glue_algorithm_names,PROVIDER ",output=der,structure=SubjectPublicKeyInfo",encode_DER_functions }, { glue_algorithm_names,PROVIDER ",output=pem,structure=SubjectPublicKeyInfo",encode_PEM_functions }, { 0, 0, 0 }, }; static const OSSL_ALGORITHM provider_decoder[] = { { glue_algorithm_names,PROVIDER ",input=der,structure=PrivateKeyInfo",decode_DER_functions }, { glue_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 glue_key)); } static void key_free(void *keydata) { OPENSSL_secure_clear_free(keydata,sizeof(struct glue_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 glue_key *K = key_new(genctx); if (!K) return 0; if (!glue_key_generate(K)) { key_free(K); return 0; } return K; } static void *key_load(const void *reference,size_t reference_sz) { struct glue_key *K = 0; if (!reference) return 0; if (reference_sz != sizeof(K)) return 0; K = *(struct glue_key **) reference; *(struct glue_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,SIGBYTES)) 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 glue_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 == GLUE_KEY_SK); return result; } static int key_match(const void *keydata1,const void *keydata2,int selection) { const struct glue_key *K1 = keydata1; const struct glue_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+SKBYTES-PKBYTES,K2->data+SKBYTES-PKBYTES,PKBYTES)) return 1; if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) if ((K1->state == GLUE_KEY_SK) && (K2->state == GLUE_KEY_SK)) if (!CRYPTO_memcmp(K1->data,K2->data,SKBYTES)) return 1; return 0; } static int key_import(void *keydata,int selection,const OSSL_PARAM params[]) { struct glue_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+SKBYTES-PKBYTES; memset(K,0,sizeof(struct glue_key)); if (!OSSL_PARAM_get_octet_string(p,&ptr,PKBYTES,&len)) return 0; if (len != PKBYTES) return 0; K->state = GLUE_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 glue_key)); if (!OSSL_PARAM_get_octet_string(p,&ptr,SKBYTES,&len)) return 0; if (len != SKBYTES) return 0; K->state = GLUE_KEY_SK; } } return 1; } static int key_export(void *keydata,int selection,OSSL_CALLBACK *param_cb,void *cbarg) { struct glue_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+SKBYTES-PKBYTES,PKBYTES); } if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) if (K->state == GLUE_KEY_SK) { if (paramspos >= KEY_EXPORT_PARAMS_MAX) return 0; params[paramspos++] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PRIV_KEY,K->data,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[] = { { glue_algorithm_names,PROVIDER,key_functions }, { 0, 0, 0 }, }; // ----- signing and verification static void *signver_new(void *provctx,const char *propq) { return OPENSSL_zalloc(sizeof(struct glue_key)); } static void signver_free(void *ctx) { OPENSSL_clear_free(ctx,sizeof(struct glue_key)); } static int signver_copykey(void *ctx,const char *mdname,void *provkey,const OSSL_PARAM params[]) { if (!ctx) 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(ctx,provkey,sizeof(struct glue_key)); return 1; } static int sign(void *ctx,unsigned char *sigret,size_t *siglen,size_t sigsize,const unsigned char *tbs,size_t tbslen) { struct glue_key *K = ctx; if (!K) return 0; if (!sigret) { *siglen = SIGBYTES; return 1; } if (sigsize < SIGBYTES) return 0; if (!glue_key_sign(sigret,tbs,tbslen,K)) return 0; *siglen = SIGBYTES; return 1; } static int verify(void *ctx,const unsigned char *sig,size_t siglen,const unsigned char *tbs,size_t tbslen) { struct glue_key *K = ctx; if (siglen != SIGBYTES) return 0; if (!glue_key_verify(sig,tbs,tbslen,K)) return 0; return 1; } static const OSSL_PARAM signver_params[] = { OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_DIGEST,0,0), OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID,0,0), OSSL_PARAM_END } ; static const OSSL_PARAM *signver_gettable_params(void *provctx) { return signver_params; } static int signver_get_params(void *ctx,OSSL_PARAM params[]) { OSSL_PARAM *p = 0; p = OSSL_PARAM_locate(params,OSSL_SIGNATURE_PARAM_DIGEST); if (p && !OSSL_PARAM_set_utf8_string(p,"")) return 0; p = OSSL_PARAM_locate(params,OSSL_SIGNATURE_PARAM_ALGORITHM_ID); if (p && !OSSL_PARAM_set_octet_string(p,glue_algorithm_asn1,sizeof glue_algorithm_asn1)) return 0; return 1; } static const OSSL_DISPATCH signver_functions[] = { { OSSL_FUNC_SIGNATURE_NEWCTX, (void(*)(void)) signver_new }, { OSSL_FUNC_SIGNATURE_FREECTX, (void(*)(void)) signver_free }, { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, (void(*)(void)) signver_copykey }, { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, (void(*)(void)) sign }, { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, (void(*)(void)) signver_copykey }, { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, (void(*)(void)) verify }, { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (void(*)(void)) signver_gettable_params }, { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void(*)(void)) signver_get_params }, { 0, 0 }, }; static const OSSL_ALGORITHM provider_signatures[] = { { glue_algorithm_names,PROVIDER,signver_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_SIGNATURE) return provider_signatures; 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,glue_provider_name)) return 0; p = OSSL_PARAM_locate(params,OSSL_PROV_PARAM_VERSION); if (p && !OSSL_PARAM_set_utf8_ptr(p,glue_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; }
Attachment:
test.sh
Description: Bourne shell script
Attachment:
signature.asc
Description: PGP signature