HardenedBSD/crypto/openssl/providers/implementations/kdfs/sshkdf.c
Pierre Pronchery b077aed33b Merge OpenSSL 3.0.9
Migrate to OpenSSL 3.0 in advance of FreeBSD 14.0.  OpenSSL 1.1.1 (the
version we were previously using) will be EOL as of 2023-09-11.

Most of the base system has already been updated for a seamless switch
to OpenSSL 3.0.  For many components we've added
`-DOPENSSL_API_COMPAT=0x10100000L` to CFLAGS to specify the API version,
which avoids deprecation warnings from OpenSSL 3.0.  Changes have also
been made to avoid OpenSSL APIs that were already deprecated in OpenSSL
1.1.1.  The process of updating to contemporary APIs can continue after
this merge.

Additional changes are still required for libarchive and Kerberos-
related libraries or tools; workarounds will immediately follow this
commit.  Fixes are in progress in the upstream projects and will be
incorporated when those are next updated.

There are some performance regressions in benchmarks (certain tests in
`openssl speed`) and in some OpenSSL consumers in ports (e.g.  haproxy).
Investigation will continue for these.

Netflix's testing showed no functional regression and a rather small,
albeit statistically significant, increase in CPU consumption with
OpenSSL 3.0.

Thanks to ngie@ and des@ for updating base system components, to
antoine@ and bofh@ for ports exp-runs and port fixes/workarounds, and to
Netflix and everyone who tested prior to commit or contributed to this
update in other ways.

PR:		271615
PR:		271656 [exp-run]
Relnotes:	Yes
Sponsored by:	The FreeBSD Foundation
2023-06-23 18:53:36 -04:00

303 lines
9.0 KiB
C

/*
* Copyright 2018-2022 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/kdf.h>
#include <openssl/core_names.h>
#include <openssl/proverr.h>
#include "internal/cryptlib.h"
#include "internal/numbers.h"
#include "crypto/evp.h"
#include "prov/provider_ctx.h"
#include "prov/providercommon.h"
#include "prov/implementations.h"
#include "prov/provider_util.h"
/* See RFC 4253, Section 7.2 */
static OSSL_FUNC_kdf_newctx_fn kdf_sshkdf_new;
static OSSL_FUNC_kdf_freectx_fn kdf_sshkdf_free;
static OSSL_FUNC_kdf_reset_fn kdf_sshkdf_reset;
static OSSL_FUNC_kdf_derive_fn kdf_sshkdf_derive;
static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_sshkdf_settable_ctx_params;
static OSSL_FUNC_kdf_set_ctx_params_fn kdf_sshkdf_set_ctx_params;
static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_sshkdf_gettable_ctx_params;
static OSSL_FUNC_kdf_get_ctx_params_fn kdf_sshkdf_get_ctx_params;
static int SSHKDF(const EVP_MD *evp_md,
const unsigned char *key, size_t key_len,
const unsigned char *xcghash, size_t xcghash_len,
const unsigned char *session_id, size_t session_id_len,
char type, unsigned char *okey, size_t okey_len);
typedef struct {
void *provctx;
PROV_DIGEST digest;
unsigned char *key; /* K */
size_t key_len;
unsigned char *xcghash; /* H */
size_t xcghash_len;
char type; /* X */
unsigned char *session_id;
size_t session_id_len;
} KDF_SSHKDF;
static void *kdf_sshkdf_new(void *provctx)
{
KDF_SSHKDF *ctx;
if (!ossl_prov_is_running())
return NULL;
if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
else
ctx->provctx = provctx;
return ctx;
}
static void kdf_sshkdf_free(void *vctx)
{
KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx;
if (ctx != NULL) {
kdf_sshkdf_reset(ctx);
OPENSSL_free(ctx);
}
}
static void kdf_sshkdf_reset(void *vctx)
{
KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx;
void *provctx = ctx->provctx;
ossl_prov_digest_reset(&ctx->digest);
OPENSSL_clear_free(ctx->key, ctx->key_len);
OPENSSL_clear_free(ctx->xcghash, ctx->xcghash_len);
OPENSSL_clear_free(ctx->session_id, ctx->session_id_len);
memset(ctx, 0, sizeof(*ctx));
ctx->provctx = provctx;
}
static int sshkdf_set_membuf(unsigned char **dst, size_t *dst_len,
const OSSL_PARAM *p)
{
OPENSSL_clear_free(*dst, *dst_len);
*dst = NULL;
*dst_len = 0;
return OSSL_PARAM_get_octet_string(p, (void **)dst, 0, dst_len);
}
static int kdf_sshkdf_derive(void *vctx, unsigned char *key, size_t keylen,
const OSSL_PARAM params[])
{
KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx;
const EVP_MD *md;
if (!ossl_prov_is_running() || !kdf_sshkdf_set_ctx_params(ctx, params))
return 0;
md = ossl_prov_digest_md(&ctx->digest);
if (md == NULL) {
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
return 0;
}
if (ctx->key == NULL) {
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
return 0;
}
if (ctx->xcghash == NULL) {
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_XCGHASH);
return 0;
}
if (ctx->session_id == NULL) {
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SESSION_ID);
return 0;
}
if (ctx->type == 0) {
ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_TYPE);
return 0;
}
return SSHKDF(md, ctx->key, ctx->key_len,
ctx->xcghash, ctx->xcghash_len,
ctx->session_id, ctx->session_id_len,
ctx->type, key, keylen);
}
static int kdf_sshkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
{
const OSSL_PARAM *p;
KDF_SSHKDF *ctx = vctx;
OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
if (params == NULL)
return 1;
if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
return 0;
if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL)
if (!sshkdf_set_membuf(&ctx->key, &ctx->key_len, p))
return 0;
if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_XCGHASH))
!= NULL)
if (!sshkdf_set_membuf(&ctx->xcghash, &ctx->xcghash_len, p))
return 0;
if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_SESSION_ID))
!= NULL)
if (!sshkdf_set_membuf(&ctx->session_id, &ctx->session_id_len, p))
return 0;
if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_TYPE))
!= NULL) {
const char *kdftype;
if (!OSSL_PARAM_get_utf8_string_ptr(p, &kdftype))
return 0;
/* Expect one character (byte in this case) */
if (kdftype == NULL || p->data_size != 1)
return 0;
if (kdftype[0] < 65 || kdftype[0] > 70) {
ERR_raise(ERR_LIB_PROV, PROV_R_VALUE_ERROR);
return 0;
}
ctx->type = kdftype[0];
}
return 1;
}
static const OSSL_PARAM *kdf_sshkdf_settable_ctx_params(ossl_unused void *ctx,
ossl_unused void *p_ctx)
{
static const OSSL_PARAM known_settable_ctx_params[] = {
OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0),
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SSHKDF_XCGHASH, NULL, 0),
OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SSHKDF_SESSION_ID, NULL, 0),
OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_SSHKDF_TYPE, NULL, 0),
OSSL_PARAM_END
};
return known_settable_ctx_params;
}
static int kdf_sshkdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
{
OSSL_PARAM *p;
if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
return OSSL_PARAM_set_size_t(p, SIZE_MAX);
return -2;
}
static const OSSL_PARAM *kdf_sshkdf_gettable_ctx_params(ossl_unused void *ctx,
ossl_unused void *p_ctx)
{
static const OSSL_PARAM known_gettable_ctx_params[] = {
OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
OSSL_PARAM_END
};
return known_gettable_ctx_params;
}
const OSSL_DISPATCH ossl_kdf_sshkdf_functions[] = {
{ OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_sshkdf_new },
{ OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_sshkdf_free },
{ OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_sshkdf_reset },
{ OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_sshkdf_derive },
{ OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
(void(*)(void))kdf_sshkdf_settable_ctx_params },
{ OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_sshkdf_set_ctx_params },
{ OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
(void(*)(void))kdf_sshkdf_gettable_ctx_params },
{ OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_sshkdf_get_ctx_params },
{ 0, NULL }
};
static int SSHKDF(const EVP_MD *evp_md,
const unsigned char *key, size_t key_len,
const unsigned char *xcghash, size_t xcghash_len,
const unsigned char *session_id, size_t session_id_len,
char type, unsigned char *okey, size_t okey_len)
{
EVP_MD_CTX *md = NULL;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int dsize = 0;
size_t cursize = 0;
int ret = 0;
md = EVP_MD_CTX_new();
if (md == NULL)
return 0;
if (!EVP_DigestInit_ex(md, evp_md, NULL))
goto out;
if (!EVP_DigestUpdate(md, key, key_len))
goto out;
if (!EVP_DigestUpdate(md, xcghash, xcghash_len))
goto out;
if (!EVP_DigestUpdate(md, &type, 1))
goto out;
if (!EVP_DigestUpdate(md, session_id, session_id_len))
goto out;
if (!EVP_DigestFinal_ex(md, digest, &dsize))
goto out;
if (okey_len < dsize) {
memcpy(okey, digest, okey_len);
ret = 1;
goto out;
}
memcpy(okey, digest, dsize);
for (cursize = dsize; cursize < okey_len; cursize += dsize) {
if (!EVP_DigestInit_ex(md, evp_md, NULL))
goto out;
if (!EVP_DigestUpdate(md, key, key_len))
goto out;
if (!EVP_DigestUpdate(md, xcghash, xcghash_len))
goto out;
if (!EVP_DigestUpdate(md, okey, cursize))
goto out;
if (!EVP_DigestFinal_ex(md, digest, &dsize))
goto out;
if (okey_len < cursize + dsize) {
memcpy(okey + cursize, digest, okey_len - cursize);
ret = 1;
goto out;
}
memcpy(okey + cursize, digest, dsize);
}
ret = 1;
out:
EVP_MD_CTX_free(md);
OPENSSL_cleanse(digest, EVP_MAX_MD_SIZE);
return ret;
}