The following is the specified skeleton of the provider described in Chapters 7–11. It is a
specification expressed as code, not a tested artefact: it was not compiled,
and it omits the algorithm bodies, which are the subject of the respective chapters. Error
handling is shown where it carries design content and elided as /* ... */ where it
does not.
/*
* tlsext provider -- core entry points.
* Specification skeleton accompanying Chapter 7. Not compiled.
*/
#include <openssl/core.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>
#include <openssl/params.h>
#include <openssl/evp.h>
#include <string.h>
typedef struct prov_ctx_st {
const OSSL_CORE_HANDLE *handle;
OSSL_FUNC_core_new_error_fn *core_new_error;
OSSL_FUNC_core_set_error_debug_fn *core_set_error_debug;
OSSL_FUNC_core_vset_error_fn *core_vset_error;
OSSL_LIB_CTX *delegate_libctx; /* private; see 7.4 */
EVP_MD *delegate_md;
} PROV_CTX;
/* 7.2: the incoming table is walked, never indexed. */
static PROV_CTX *prov_ctx_new(const OSSL_CORE_HANDLE *handle,
const OSSL_DISPATCH *in)
{
PROV_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
if (ctx == NULL)
return NULL;
ctx->handle = handle;
for (; in->function_id != 0; in++) {
switch (in->function_id) {
case OSSL_FUNC_CORE_NEW_ERROR:
ctx->core_new_error = OSSL_FUNC_core_new_error(in);
break;
case OSSL_FUNC_CORE_SET_ERROR_DEBUG:
ctx->core_set_error_debug = OSSL_FUNC_core_set_error_debug(in);
break;
case OSSL_FUNC_CORE_VSET_ERROR:
ctx->core_vset_error = OSSL_FUNC_core_vset_error(in);
break;
default:
break; /* unknown ids are ignored, not errors */
}
}
/* 7.4: delegation uses a private context, never the caller's. */
if ((ctx->delegate_libctx = OSSL_LIB_CTX_new()) == NULL)
goto err;
ctx->delegate_md = EVP_MD_fetch(ctx->delegate_libctx, "SHA2-256",
"provider=default");
if (ctx->delegate_md == NULL)
goto err;
return ctx;
err:
prov_ctx_free(ctx);
return NULL;
}
static void tlsext_teardown(void *provctx)
{
prov_ctx_free((PROV_CTX *)provctx); /* N4: safe on NULL */
}
/* 7.3 */
static const OSSL_ALGORITHM *tlsext_query(void *provctx, int operation_id,
int *no_cache)
{
*no_cache = 0;
switch (operation_id) {
case OSSL_OP_DIGEST: return tlsext_digests;
case OSSL_OP_CIPHER: return tlsext_ciphers;
case OSSL_OP_KEYMGMT: return tlsext_keymgmt;
case OSSL_OP_KEYEXCH: return tlsext_keyexch;
case OSSL_OP_SIGNATURE: return tlsext_signatures;
}
return NULL;
}
/* 10.5: advertisement is a separate obligation from implementation. */
static int tlsext_get_capabilities(void *provctx, const char *capability,
OSSL_CALLBACK *cb, void *arg)
{
if (OPENSSL_strcasecmp(capability, "TLS-GROUP") == 0)
return tlsext_group_capability(cb, arg);
if (OPENSSL_strcasecmp(capability, "TLS-SIGALG") == 0)
return tlsext_sigalg_capability(cb, arg);
return 0; /* capability not supported */
}
static const OSSL_DISPATCH provider_dispatch[] = {
{ OSSL_FUNC_PROVIDER_TEARDOWN, (void (*)(void))tlsext_teardown },
{ OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void (*)(void))tlsext_query },
{ OSSL_FUNC_PROVIDER_GET_CAPABILITIES,
(void (*)(void))tlsext_get_capabilities },
{ OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void (*)(void))tlsext_gettable },
{ OSSL_FUNC_PROVIDER_GET_PARAMS, (void (*)(void))tlsext_get_params },
{ 0, NULL }
};
int OSSL_provider_init(const OSSL_CORE_HANDLE *handle,
const OSSL_DISPATCH *in,
const OSSL_DISPATCH **out,
void **provctx)
{
PROV_CTX *ctx;
if ((ctx = prov_ctx_new(handle, in)) == NULL)
return 0;
*provctx = ctx;
*out = provider_dispatch;
return 1;
}
/* 9.5: distinctive names; the component is inert until asked for by name. */
static const OSSL_ALGORITHM tlsext_digests[] = {
{ "TLSEXT-HASH256", "provider=tlsext",
tlsext_digest_functions, "Experimental TLS handshake hash" },
{ NULL, NULL, NULL, NULL }
};
static const OSSL_ALGORITHM tlsext_ciphers[] = {
{ "TLSEXT-AEAD", "provider=tlsext",
tlsext_aead_functions, "Experimental AEAD for TLS record protection" },
{ NULL, NULL, NULL, NULL }
};
static const OSSL_ALGORITHM tlsext_keymgmt[] = {
{ "TLSEXT-P256", "provider=tlsext",
tlsext_ec_keymgmt_functions, "Experimental EC group key management" },
{ NULL, NULL, NULL, NULL }
};
static const OSSL_ALGORITHM tlsext_keyexch[] = {
{ "TLSEXT-P256", "provider=tlsext",
tlsext_ec_keyexch_functions, "Experimental EC key agreement" },
{ NULL, NULL, NULL, NULL }
};
/*
* 9.2: duplication must be deep. Copying the delegate handle instead of
* duplicating the delegate context is the shallow-copy error that appears
* to work until one of the two contexts is updated.
*/
typedef struct digest_ctx_st {
PROV_CTX *provctx;
EVP_MD_CTX *delegate; /* owned */
} DIGEST_CTX;
static void *digest_dupctx(void *vctx)
{
DIGEST_CTX *src = vctx, *dst;
if (src == NULL)
return NULL;
if ((dst = OPENSSL_zalloc(sizeof(*dst))) == NULL)
return NULL;
dst->provctx = src->provctx;
if ((dst->delegate = EVP_MD_CTX_new()) == NULL)
goto err;
/* duplicate the STATE, not the handle */
if (!EVP_MD_CTX_copy_ex(dst->delegate, src->delegate))
goto err;
return dst;
err:
digest_freectx(dst);
return NULL;
}
/* 9.3: the key schedule sizes itself from these answers. */
static const OSSL_PARAM *digest_gettable(void *provctx)
{
static const OSSL_PARAM table[] = {
OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_BLOCK_SIZE, NULL),
OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_SIZE, NULL),
OSSL_PARAM_END
};
return table;
}
static int digest_get_params(OSSL_PARAM params[])
{
OSSL_PARAM *p;
if ((p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE)) != NULL
&& !OSSL_PARAM_set_size_t(p, TLSEXT_DIGEST_SIZE))
return 0;
if ((p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_BLOCK_SIZE)) != NULL
&& !OSSL_PARAM_set_size_t(p, TLSEXT_DIGEST_BLOCK))
return 0;
return 1;
}
/* 10.5 and 4.3.1. The code point comes from a registry or the
* private-use range -- never from neither (10.6). */
static unsigned int group_id = TLSEXT_GROUP_CODE_POINT;
static unsigned int secbits = 128;
static int min_tls = TLS1_3_VERSION;
static int max_tls = TLS1_3_VERSION;
static int tlsext_group_capability(OSSL_CALLBACK *cb, void *arg)
{
static const OSSL_PARAM group[] = {
OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME,
"tlsext256", sizeof("tlsext256")),
OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL,
"TLSEXT-P256", sizeof("TLSEXT-P256")),
OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG,
"TLSEXT-P256", sizeof("TLSEXT-P256")),
OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, &group_id),
OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, &secbits),
OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, &min_tls),
OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, &max_tls),
OSSL_PARAM_END
};
return cb(group, arg);
}
/*
* 10.4 and 17.4.1. This is the design's most security-critical function.
* Validation happens BEFORE any operation involving the private key.
*/
static int ecdh_set_peer(void *vctx, void *vpeer)
{
KEYEXCH_CTX *ctx = vctx;
EC_KEY *peer = vpeer;
if (ctx == NULL || peer == NULL)
return 0;
/* 1. the encoding decoded to a point at all */
if (!tlsext_point_decoded(peer))
return 0;
/* 2. the point satisfies the curve equation */
if (!tlsext_point_on_curve(peer))
return 0;
/* 3. the point is not the identity */
if (tlsext_point_is_infinity(peer))
return 0;
/* 4. small-subgroup check, where the cofactor permits one */
if (!tlsext_point_order_ok(peer))
return 0;
ctx->peer = peer;
return 1;
}